Zustand(德语“状态”)是一个轻量级、高性能的 React 状态管理库,核心理念是让状态管理像使用普通的 JavaScript 对象一样简单。它不依赖 Provider 包裹组件树,不强制使用 reducer 或 action 类型,API 设计极简,学习成本几乎为零。
为什么需要 Zustand
当应用状态跨多层组件共享时,使用 useState + useContext 的组合往往面临两个问题:
- Provider 嵌套地狱:每个需要共享的状态域都需要一个 Context Provider,层层包裹,影响代码可读性。
- 不必要的重渲染:Context 值一旦变化,所有消费该 Context 的组件及其子组件都会重渲染,难以精准控制更新边界。
Redux 等方案能解决一些问题,但引入了大量样板代码(action 类型、reducer、connect/mapStateToProps)。Zustand 以几乎零模板的方式提供了全局状态管理能力,同时避免了上述问题。
快速上手
安装:
npm install zustand
创建一个 store:
import { create } from 'zustand';
const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));
在组件中使用:
function Counter() {
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
return (
<div>
<span>{count}</span>
<button onClick={increment}>+1</button>
</div>
);
}
不需要在最外层包裹任何 <Provider>。任何组件直接调用 useCounterStore 的 selector 函数即可读取状态或获取 action。这是 Zustand 最大的特点——store 在模块级别创建并导出,使用时直接引入,完全解耦。
核心设计:基于 Selector 的精准重渲染
Zustand 的 useStore(selector) Hook 默认会对 selector 返回的值做浅比较,只有当选中的值真正变化时才会重渲染。你可以通过 selector 精确订阅所需的最小状态片段:
// 只会因为 `count` 变化而重渲染
const count = useCounterStore((state) => state.count);
// 读取 action 的函数引用,永远不会触发重渲染(因为函数引用不变)
const increment = useCounterStore((state) => state.increment);
这让你能轻松写出性能优化的组件,无需 useMemo 或 React.memo。
实际应用示例:用户认证状态
const useAuthStore = create((set) => ({
user: null,
token: null,
login: async (username, password) => {
const res = await api.login(username, password);
set({ user: res.user, token: res.token });
},
logout: () => set({ user: null, token: null }),
}));
// 在需要用户信息的组件中
function UserAvatar() {
const user = useAuthStore((state) => state.user);
if (!user) return null;
return <img src={user.avatar} alt={user.name} />;
}
// 在需要登录操作的组件中
function LoginButton() {
const login = useAuthStore((state) => state.login);
return <button onClick={() => login('admin', '123')}>登录</button>;
}
多个 store 可以直接创建并独立使用,互不影响,天然支持模块化。
进阶用法:中间件与派生状态
Zustand 内置了多种中间件,比如 persist(持久化到 localStorage)、immer(不可变数据简化更新)。使用方式是通过高阶函数包装:
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useTodoStore = create(
persist(
(set) => ({
todos: [],
addTodo: (text) =>
set((state) => ({
todos: [...state.todos, { id: Date.now(), text, done: false }],
})),
toggleTodo: (id) =>
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, done: !todo.done } : todo
),
})),
}),
{ name: 'todo-storage' } // 持久化到 localStorage 的 key
)
);
如此即可获得自动持久化能力,无需额外代码。
对于派生状态(如从 todo 列表中筛选未完成的数量),可以直接在 store 中定义,也可以利用 selector 计算:
// 在 selector 中动态计算
const undoneCount = useTodoStore((state) =>
state.todos.filter((t) => !t.done).length
);
与 Context 方案和 Redux 的对比
| 特性 | Zustand | Context + useReducer | Redux (Toolkit) |
|------|---------|----------------------|-----------------|
| Provider 包裹 | 不需要 | 需要 | 需要 |
| 样板代码 | 极少 | 中等 | 中等(但有 Toolkit 简化) |
| 性能 | 基于 selector 浅比较 | 默认全量重渲染,需手动优化 | 基于 selector 浅比较 |
| 中间件生态 | 轻量但够用 | 无内置 | 丰富(devtools, thunk 等) |
| 学习曲线 | 极低 | 低 | 中高 |
| 适用场景 | 中小到大型应用 | 小型局部状态 | 大型复杂应用 |
Zustand 尤其适合不想引入过多概念、希望快速实现的团队。即使是大型项目,多个独立 store 的组合也能很好地管理复杂状态。部分开发者甚至将其与 React Query(服务端状态)配合,完全替代 Redux。
常见注意事项
- Store 应该是单例:通过
create创建的 store 是模块级单例,在服务端渲染(SSR)时需要注意隔离,Zustand 提供createStore配合 Context 来处理此情况。 - 避免在 selector 中返回新对象:默认使用
Object.is浅比较,如果 selector 每次返回一个新对象,会导致不必要的重渲染。需要时可通过shallow比较函数或使用useShallowHook(Zustand v4+)来正确处理。 - 异步操作与 set 调用:在异步回调中直接使用
set是安全的,Zustand 的set与 React 的批量更新兼容,在 React 18 中会自动批处理。
总结
Zustand 以“API 极简、零 Provider、精准重渲染”的特点,成为 React 社区最受欢迎的非 Redux 状态管理方案之一。它让全局状态的使用就像调用一个自定义 Hook 一样自然,在中小型项目和微前端架构中表现尤为出色。如果你厌倦了 Redux 的模板代码,又觉得 Context 的性能问题难以驾驭,Zustand 是非常值得尝试的选择。