# Zustand 状态管理学习指南
## 1. 什么是 Zustand?
Zustand(德语“状态”的意思)是一个基于 Hooks 的轻量级、极简的 React 状态管理库。它的核心优势包括:
- **轻量极简**:gzip 压缩后仅约 1KB,学习成本极低。
- **无 Provider 包裹**:不需要像 Redux 或 Context 那样在根组件嵌套 Provider,使用更加灵活。
- **按需订阅**:通过 Selector(选择器)机制,精准订阅所需状态,避免不必要的组件重渲染。
- **TypeScript 友好**:提供完善的类型推导。
- **强大的中间件生态**:内置支持 `persist`(持久化)、`devtools`(调试)、`immer`(不可变数据)等。
## 2. 快速上手
### 2.1 安装
```bash
npm install zustand
# 或者
yarn add zustand
2.2 创建 Store
使用 create 函数创建一个 Store。Store 本质上是一个自定义 Hook,里面可以存放基础变量、对象、函数等任意内容。
import { create } from 'zustand';
const useBearStore = create((set) => ({
bears: 0,
// 依赖旧状态更新时,推荐使用函数式更新(避免闭包陷阱)
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
// 直接赋值(无需依赖旧状态)
removeAllBears: () => set({ bears: 0 }),
}));
2.3 在组件中使用
在组件中调用 Store Hook,并通过 Selector 提取所需状态:
function BearCounter() {
// 仅订阅 bears,只有 bears 变化时组件才会重渲染
const bears = useBearStore((state) => state.bears);
return ;
}
function Controls() {
// 仅订阅 action 函数
const increasePopulation = useBearStore((state) => state.increasePopulation);
return ;
}
3. 核心进阶用法
3.1 异步状态管理
Zustand 原生支持异步操作,无需引入额外的中间件。直接在 action 中编写异步逻辑,并在请求完成后调用 set 更新状态即可。
const useUserStore = create((set) => ({
user: null,
loading: false,
fetchUser: async (userId) => {
set({ loading: true });
try {
const res = await fetch(`/api/user/${userId}`);
const data = await res.json();
set({ user: data, loading: false });
} catch (error) {
console.error(error);
set({ loading: false });
}
},
}));
3.2 嵌套状态与 Immer 集成
Zustand 的 set 方法默认只进行浅合并(Shallow Merge)。如果状态是深度嵌套的对象,手动使用展开运算符(...)会非常繁琐。此时可以结合 immer 中间件,像修改普通对象一样修改状态:
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
const useDeepStore = create(
immer((set) => ({
deep: { nested: { obj: { count: 0 } } },
inc: () =>
set((state) => {
// 直接修改,Immer 会自动处理不可变更新
state.deep.nested.obj.count += 1;
}),
}))
);
4. 性能优化最佳实践
4.1 精准订阅(避免全量重渲染)
错误写法:直接解构整个 Store,会导致任意状态变化时组件都会重渲染。
const { bears, increasePopulation } = useBearStore(); // 不推荐
🟢 正确写法:使用 Selector 提取具体值。
const bears = useBearStore((state) => state.bears);
4.2 订阅多个状态与浅比较
当需要同时订阅多个状态时,如果直接返回一个对象,由于每次渲染都会生成新的对象引用,会导致无限重渲染。此时需要引入 shallow 比较函数:
import { shallow } from 'zustand/shallow';
const { bears, user } = useBearStore(
(state) => ({ bears: state.bears, user: state.user }),
shallow // 浅比较:对比对象内属性的值,而非引用地址
);
4.3 按业务模块拆分 Store
当应用规模变大时,官方推荐将 Store 按业务模块拆分,而不是维护一个巨大的全局 Store:
// store/userStore.js
export const useUserStore = create((set) => ({ /* 用户状态 */ }));
// store/cartStore.js
export const useCartStore = create((set) => ({ /* 购物车状态 */ }));
5. 实用中间件与调试
5.1 状态持久化 (Persist)
将状态自动同步到 localStorage 或 sessionStorage,刷新页面后状态不丢失:
import { persist } from 'zustand/middleware';
const useThemeStore = create(
persist(
(set) => ({
theme: 'light',
toggleTheme: () => set((state) => ({
theme: state.theme === 'light' ? 'dark' : 'light'
})),
}),
{ name: 'theme-storage' } // 本地存储的 key
)
);
5.2 接入 Redux DevTools
借助 devtools 中间件,可以在浏览器控制台的 Redux DevTools 面板中查看状态变更历史、时间旅行调试:
import { devtools } from 'zustand/middleware';
const useStore = create(
devtools((set) => ({
count: 0,
inc: () => set((state) => ({ count: state.count + 1 }), false, 'increment'),
}))
);
6. 官方强调的注意事项
- 依赖旧状态必须用函数参数:更新状态时,如果新值依赖旧值,务必使用
set((state) => ...)的函数形式,防止因闭包问题获取到过期的旧状态。 - 批量更新:Zustand 内部会自动对多次连续的
set进行批量合并处理,无需手动使用unstable_batchedUpdates,有效避免了多次重渲染。 - 异步请求与组件卸载:在组件卸载时,如果异步请求尚未完成,直接调用
set可能会触发 React 警告。建议在useEffect的清理函数中使用AbortController取消未完成的请求。
