客户端状态管理
Zustand、MobX 对比 Context。
35 分钟
客户端状态管理
组件内的 useState 管不了 跨路由、跨模块 的共享状态。react-context 能抬一层,但大应用里 Provider 嵌套与重渲染 会拖慢体验。本课对比 Context、Zustand、MobX 的适用边界。
学习目标
- 区分本地 UI 状态与全局客户端状态
- 理解 Zustand「小 store + 选择器」与 MobX「可观察对象」的差异
- 知道何时不必引入第三方状态库
状态分类
| 类型 | 示例 | 推荐 |
|---|---|---|
| 本地 UI | 输入框、Modal 开关 | useState |
| 跨组件共享 | 主题、侧栏折叠 | Context 或 Zustand |
| 服务器数据 | 课程列表、用户资料 | TanStack Query(下节) |
| URL 状态 | 筛选、分页 | React Router / Next.js searchParams |
反模式:把 API 响应整包塞进 Zustand,却不用 Query 做缓存与失效——下节会专门讲服务端状态。
Context 何时够用
- 树不深、更新不频繁(主题、locale)
- 消费者少,或可用
useMemo稳定 value
Context 痛点:任意 value 变化 → 所有 consumer 重渲染。拆多个 Context 或配合 useReducer 可缓解,但样板代码会涨。
Zustand
轻量 store,组件 按 selector 订阅 切片,避免无关更新。
import { create } from "zustand";
type UiStore = {
sidebarOpen: boolean;
toggleSidebar: () => void;
};
export const useUiStore = create<UiStore>((set) => ({
sidebarOpen: true,
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
}));
// 组件里只订阅 sidebarOpen
const open = useUiStore((s) => s.sidebarOpen);
适合:platform 侧栏、草稿编辑器工具栏、多页共享的 UI 偏好。
MobX
可观察对象 + 自动依赖追踪,写法接近「改普通对象属性」。学习曲线与「魔法」感高于 Zustand;老项目或团队已标准化时常见。
| Zustand | MobX | |
|---|---|---|
| 心智模型 | 显式 store + set | observable + action |
| 包体积 | 小 | 中 |
| TypeScript | 友好 | 需装饰器或 boilerplate |
| 团队新人 | 易上手 | 需理解 reaction |
与 Next.js 的边界
App Router 下 默认 Server Component。全局客户端 store 应放在 子树,且 ——见 的数据边界。