大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
客户端状态管理
组件内的 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 应放在 'use client' 子树,且 不要把大对象从 server props 无脑灌进 store——见 nextjs-essentials 的数据边界。
练习
- 用 Zustand 实现「最近浏览课时」列表(最多 5 条)
- 对比:同一功能用 Context 写,感受 Provider 与重渲染
- 读 platform 源码,找一处适合 store 而非 prop drilling 的状态
小结
本地用 State,轻共享用 Context 或 Zustand,服务器数据交给 Query。 下一课 TanStack Query 与 RTK Query 管服务端状态。