一、核心结论: 状态管理的本质
不仅仅是 客户端,它内置了完整的客户端状态管理方案。你不需要额外引入 Redux 或 MobX,就能统一管理远程数据和本地状态。
核心机制:
1. 规范化缓存:所有通过 查询获取的数据都会自动存入 ,并按照 和 id 进行标准化,保证数据一致性。
2. 本地状态:通过 (推荐)或 local (已标记为 )来管理不经过 服务的纯前端状态。
这种架构让“远程数据”与“本地 UI 状态”共用同一套响应式系统,极大简化了数据流。
二、基础配置:30 秒搭建状态管理环境
2.1 安装依赖
npm @/
2.2 创建带有状态管理能力的 实例
{ , , } from '@/';
// 创建 (全局响应式状态)
const = <[]>([]);
const = new ({
uri: ';,
cache: new ({
: {
// 自定义类型策略,将本地状态合并到缓存字段中
Query: {
: {
: {
read() {
();
}
}
}
}
}
})
});
要点: 创建的变量是响应式的,任何读取它的组件都会自动在变量变化时重新渲染。
三、远程状态管理:缓存策略与数据更新
3.1 缓存规范化机制
会自动将查询结果按 和 id(或 _id)进行拆解存储。
示例数据:
query {
todos {
id
title
}
}
返回的 todos 数组中每个 todo 对象会被单独缓存,后续任何修改 todo 的操作(如 返回更新的 todo)都会自动更新所有查询中该 todo 的位置。
3.2 手动更新缓存
当你执行 后,需要更新列表时,可使用 cache. 或 cache.。
const [] = (, {
(cache, { data: { } }) {
cache.({
: {
todos( = []) {
const = cache.({
data: ,
: gql
on Todo {
id
title
}
});
[..., ];
}
}
});
}
});
3.3 乐观响应
提升用户体验,先更新 UI,后同步服务器:
const [] = (, {
: {
: {
id: 'temp-id',
title: input,
: false,
: 'Todo'
}
},
(cache, { data: { } }) {
// 正常更新缓存
}
});
四、本地状态管理: 全面替代
从 3.0 开始,官方推荐使用 管理本地状态,无需定义本地查询或 。
4.1 定义和使用
// state.ts
{ } from '@/';
const = (false);
const = ({ theme: 'light', : true });
4.2 在 React 组件中读写
{ } from '@/';
{ , } from './state';
() {
const = ();
const = ();
const = () => {
(false);
({ ..., theme: 'dark' });
};
(
<div>
{ ? '已登录' : '未登录'}
< ={}>退出</>
</div>
);
}
4.3 将 集成到 查询中(可选)
如果你希望通过 查询获取本地状态(例如在 Query 字段中返回购物车),可以使用 read 函数:
const cache = new ({
: {
Query: {
: {
: {
read() {
();
}
}
}
}
}
});
然后在组件中就可以正常使用 查询 。
五、完整示例:购物车应用状态管理
5.1 定义全局状态和客户端
// .ts
{ , , } from '@/';
const = <[]>([]);
const = new ({
uri: ';,
cache: new ({
: {
Query: {
: {
: {
read() {
();
}
}
}
}
}
})
});
5.2 添加商品到购物车(仅本地操作)
{ } from '@/';
{ } from './';
({ id }: { id: }) {
const = ();
const = .(id);
const = () => {
([..., id]);
};
const = () => {
(.(item => item !== id));
};
(
<div>
{ ? (
< ={}>移出购物车</>
) : (
< ={}>加入购物车</>
)}
</div>
);
}
5.3 购物车页面读取本地状态 + 远程商品详情
{ , } from '@/';
{ } from './';
{ gql } from '@/';
const = gql
query ($ids: [ID!]!) {
(ids: $ids) {
id
name
price
}
}
;
Cart() {
const = ();
const { data, } = (, {
: { ids: },
skip: . === 0,
});
if () <div>加载中...</div>;
(
<ul>
{data?..map( => (
<li key={.id}>
{.name} - ${.price}
</li>
))}
</ul>
);
}
六、常见疑难与最佳实践
6.1 问题:为什么缓存更新后界面不刷新?
原因:直接修改了缓存对象但没有触发变更通知。应使用 cache. 或返回新对象的 更新。
解决:确保 的 函数正确写入缓存引用。
6.2 问题: 变化后组件未重渲染
原因:未使用 钩子读取变量。
解决:必须用 () 而不是直接读取变量值。
6.3 问题:缓存数据过大导致性能下降
解决:
配置 evict 策略或设置 cache.gc() 手动回收。
使用 中的 merge 函数控制列表合并逻辑,避免重复数据。
6.4 最佳实践清单
| 场景 | 推荐方案 |
|---|---|
| 远程数据 | 依赖规范化缓存 + 通过 更新 |
| UI 状态(弹窗、tab) | |
| 全局用户信息 | |
| 表单临时数据 | 组件内部 state 或 (如需跨组件) |
| 分页/列表合并 | 自定义 merge 函数 |
| 持久化状态 | 配合 写入 的初始值 |
七、权威参考与扩展阅读
官方文档: 状态管理
迁移指南:从 Redux 迁移到 状态管理的最佳实践可参考官方迁移案例。
> 所有代码示例基于 3.8+ 版本,并遵循官方当前推荐实践( 为本地状态首选方案)。

