Zustand 全局状态管理

Zustand 的核心用法:create + persist 实现全局状态、选择性订阅优化性能、与 TanStack Query 的分工边界。

#type / howto #status / growing #tech / dev / frontend #resource / react

[!info] related notes

Zustand 全局状态管理

核心问题

React 应用中有两类状态:客户端状态(UI 开关、表单、认证 token)和服务端状态(API 返回的数据)。客户端状态需要一个全局共享、可持久化的方案。

为什么选 Zustand

维度ReduxContextZustand
模板代码极少
性能差(全量重渲染)
持久化需要插件手动内置 persist
学习曲线极低

Zustand 的核心卖点:一个 create 函数搞定,没有 Provider 包裹,选择性订阅。

最小用法

const useAuthStore = create<AuthState>()((set, get) => ({
  user: null,
  accessToken: null,
  isAuthenticated: false,

  login: async (email, password) => {
    const data = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ email, password }) });
    set({ accessToken: data.access_token, isAuthenticated: true });
  },

  logout: () => set({ user: null, accessToken: null, isAuthenticated: false }),
}));

组件中:

const user = useAuthStore((state) => state.user);      // 选择性订阅
const login = useAuthStore((state) => state.login);     // 方法引用不变,不触发重渲染

持久化

const useAuthStore = create<AuthState>()(
  persist(
    (set, get) => ({ ... }),
    {
      name: 'auth-storage',  // localStorage key
      partialize: (state) => ({
        accessToken: state.accessToken,
        refreshToken: state.refreshToken,
        isAuthenticated: state.isAuthenticated,
        user: state.user,
        // 不持久化 isLoading、error 等临时状态
      }),
    },
  ),
);

选择性订阅

// ✅ 只订阅需要的字段
const user = useAuthStore((state) => state.user);

// ❌ 订阅整个 store,任何字段变化都重渲染
const store = useAuthStore();

与 TanStack Query 的分工

维度ZustandTanStack Query
数据来源客户端产生服务端获取
例子认证 token、UI 开关、表单草稿用户列表、会话数据、评估报告
缓存无(手动管理)自动缓存 + 失效
持久化persist 中间件

简单判断:需要持久化到 localStorage → Zustand;来自 API → TanStack Query

常见错误

在组件外用 hook

// ❌ 组件外不能用 hook
const token = useAuthStore((s) => s.accessToken);

// ✅ 用 getState()
const { accessToken } = useAuthStore.getState();

store 里做异步操作不设 loading

// ❌ 没有 loading 状态
login: async (email, password) => {
  const data = await fetch(...);
  set({ accessToken: data.access_token });
}

// ✅ 设 loading 和 error
login: async (email, password) => {
  set({ isLoading: true, error: null });
  try {
    const data = await fetch(...);
    set({ accessToken: data.access_token, isLoading: false });
  } catch (e) {
    set({ error: e.message, isLoading: false });
  }
}
创建于 2026/6/25 更新于 2026/6/25