人人都会AI编程

Zustand:极简 API、无 Provider 的状态管理

更新时间:2026-07-11

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);

这让你能轻松写出性能优化的组件,无需 useMemoReact.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 比较函数或使用 useShallow Hook(Zustand v4+)来正确处理。
  • 异步操作与 set 调用:在异步回调中直接使用 set 是安全的,Zustand 的 set 与 React 的批量更新兼容,在 React 18 中会自动批处理。

总结

Zustand 以“API 极简、零 Provider、精准重渲染”的特点,成为 React 社区最受欢迎的非 Redux 状态管理方案之一。它让全局状态的使用就像调用一个自定义 Hook 一样自然,在中小型项目和微前端架构中表现尤为出色。如果你厌倦了 Redux 的模板代码,又觉得 Context 的性能问题难以驾驭,Zustand 是非常值得尝试的选择。