zustand

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Zustand State Management

Zustand 状态管理

Lightweight, flexible state management for React.
适用于React的轻量、灵活状态管理工具。

When NOT to Use

不适用场景

  • Server state → Use TanStack Query
  • Form state → Use React Hook Form
  • URL state → Use router params
  • 服务端状态 → 使用TanStack Query
  • 表单状态 → 使用React Hook Form
  • URL状态 → 使用路由参数

Instructions

使用指南

1. Basic Store

1. 基础Store

typescript
// store/useAuthStore.ts
import { create } from 'zustand';

interface AuthState {
  user: User | null;
  token: string | null;
  isAuthenticated: boolean;
  
  // Actions
  login: (user: User, token: string) => void;
  logout: () => void;
  updateUser: (updates: Partial<User>) => void;
}

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

  login: (user, token) => set({
    user,
    token,
    isAuthenticated: true,
  }),

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

  updateUser: (updates) => set((state) => ({
    user: state.user ? { ...state.user, ...updates } : null,
  })),
}));
typescript
// store/useAuthStore.ts
import { create } from 'zustand';

interface AuthState {
  user: User | null;
  token: string | null;
  isAuthenticated: boolean;
  
  // 操作方法
  login: (user: User, token: string) => void;
  logout: () => void;
  updateUser: (updates: Partial<User>) => void;
}

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

  login: (user, token) => set({
    user,
    token,
    isAuthenticated: true,
  }),

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

  updateUser: (updates) => set((state) => ({
    user: state.user ? { ...state.user, ...updates } : null,
  })),
}));

2. Usage in Components

2. 在组件中使用

tsx
function Header() {
  // ✅ Select only what you need
  const user = useAuthStore((state) => state.user);
  const logout = useAuthStore((state) => state.logout);

  return (
    <header>
      <span>{user?.name}</span>
      <button onClick={logout}>Logout</button>
    </header>
  );
}

// ✅ Multiple selectors
function Profile() {
  const { user, updateUser } = useAuthStore((state) => ({
    user: state.user,
    updateUser: state.updateUser,
  }));
}
tsx
function Header() {
  // ✅ 仅选择所需状态
  const user = useAuthStore((state) => state.user);
  const logout = useAuthStore((state) => state.logout);

  return (
    <header>
      <span>{user?.name}</span>
      <button onClick={logout}>Logout</button>
    </header>
  );
}

// ✅ 多选择器
function Profile() {
  const { user, updateUser } = useAuthStore((state) => ({
    user: state.user,
    updateUser: state.updateUser,
  }));
}

3. Persist Middleware (localStorage)

3. 持久化中间件(localStorage)

typescript
// store/useSettingsStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface SettingsState {
  theme: 'light' | 'dark';
  language: 'en' | 'uz' | 'ru';
  sidebarOpen: boolean;
  
  setTheme: (theme: 'light' | 'dark') => void;
  setLanguage: (lang: 'en' | 'uz' | 'ru') => void;
  toggleSidebar: () => void;
}

export const useSettingsStore = create<SettingsState>()(
  persist(
    (set) => ({
      theme: 'light',
      language: 'en',
      sidebarOpen: true,

      setTheme: (theme) => set({ theme }),
      setLanguage: (language) => set({ language }),
      toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
    }),
    {
      name: 'settings-storage', // localStorage key
      partialize: (state) => ({
        theme: state.theme,
        language: state.language,
        // Don't persist sidebarOpen
      }),
    }
  )
);
typescript
// store/useSettingsStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface SettingsState {
  theme: 'light' | 'dark';
  language: 'en' | 'uz' | 'ru';
  sidebarOpen: boolean;
  
  setTheme: (theme: 'light' | 'dark') => void;
  setLanguage: (lang: 'en' | 'uz' | 'ru') => void;
  toggleSidebar: () => void;
}

export const useSettingsStore = create<SettingsState>()(
  persist(
    (set) => ({
      theme: 'light',
      language: 'en',
      sidebarOpen: true,

      setTheme: (theme) => set({ theme }),
      setLanguage: (language) => set({ language }),
      toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
    }),
    {
      name: 'settings-storage', // localStorage 键名
      partialize: (state) => ({
        theme: state.theme,
        language: state.language,
        // 不持久化sidebarOpen状态
      }),
    }
  )
);

4. Computed Values (Derived State)

4. 计算值(派生状态)

typescript
// store/useCartStore.ts
interface CartState {
  items: CartItem[];
  
  // Actions
  addItem: (item: Product) => void;
  removeItem: (id: string) => void;
  clearCart: () => void;
  
  // Computed (not stored, calculated)
  getTotalItems: () => number;
  getTotalPrice: () => number;
}

export const useCartStore = create<CartState>((set, get) => ({
  items: [],

  addItem: (product) => set((state) => {
    const existing = state.items.find((i) => i.id === product.id);
    if (existing) {
      return {
        items: state.items.map((i) =>
          i.id === product.id ? { ...i, quantity: i.quantity + 1 } : i
        ),
      };
    }
    return { items: [...state.items, { ...product, quantity: 1 }] };
  }),

  removeItem: (id) => set((state) => ({
    items: state.items.filter((i) => i.id !== id),
  })),

  clearCart: () => set({ items: [] }),

  // Computed - use get() to access current state
  getTotalItems: () => get().items.reduce((sum, i) => sum + i.quantity, 0),
  getTotalPrice: () => get().items.reduce((sum, i) => sum + i.price * i.quantity, 0),
}));
typescript
// store/useCartStore.ts
interface CartState {
  items: CartItem[];
  
  // 操作方法
  addItem: (item: Product) => void;
  removeItem: (id: string) => void;
  clearCart: () => void;
  
  // 计算属性(不存储,实时计算)
  getTotalItems: () => number;
  getTotalPrice: () => number;
}

export const useCartStore = create<CartState>((set, get) => ({
  items: [],

  addItem: (product) => set((state) => {
    const existing = state.items.find((i) => i.id === product.id);
    if (existing) {
      return {
        items: state.items.map((i) =>
          i.id === product.id ? { ...i, quantity: i.quantity + 1 } : i
        ),
      };
    }
    return { items: [...state.items, { ...product, quantity: 1 }] };
  }),

  removeItem: (id) => set((state) => ({
    items: state.items.filter((i) => i.id !== id),
  })),

  clearCart: () => set({ items: [] }),

  // 计算属性 - 使用get()获取当前状态
  getTotalItems: () => get().items.reduce((sum, i) => sum + i.quantity, 0),
  getTotalPrice: () => get().items.reduce((sum, i) => sum + i.price * i.quantity, 0),
}));

5. Immer Middleware (Immutable Updates)

5. Immer中间件(不可变更新)

typescript
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';

interface TodoState {
  todos: Todo[];
  addTodo: (text: string) => void;
  toggleTodo: (id: string) => void;
}

export const useTodoStore = create<TodoState>()(
  immer((set) => ({
    todos: [],

    addTodo: (text) => set((state) => {
      // ✅ Mutate directly with immer
      state.todos.push({
        id: crypto.randomUUID(),
        text,
        completed: false,
      });
    }),

    toggleTodo: (id) => set((state) => {
      const todo = state.todos.find((t) => t.id === id);
      if (todo) {
        todo.completed = !todo.completed;
      }
    }),
  }))
);
typescript
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';

interface TodoState {
  todos: Todo[];
  addTodo: (text: string) => void;
  toggleTodo: (id: string) => void;
}

export const useTodoStore = create<TodoState>()(
  immer((set) => ({
    todos: [],

    addTodo: (text) => set((state) => {
      // ✅ 使用Immer直接修改状态
      state.todos.push({
        id: crypto.randomUUID(),
        text,
        completed: false,
      });
    }),

    toggleTodo: (id) => set((state) => {
      const todo = state.todos.find((t) => t.id === id);
      if (todo) {
        todo.completed = !todo.completed;
      }
    }),
  }))
);

6. Slices Pattern (Large Stores)

6. 切片模式(大型Store)

typescript
// store/slices/userSlice.ts
export interface UserSlice {
  user: User | null;
  setUser: (user: User) => void;
}

export const createUserSlice = (set: SetState<UserSlice>): UserSlice => ({
  user: null,
  setUser: (user) => set({ user }),
});

// store/slices/cartSlice.ts
export interface CartSlice {
  items: CartItem[];
  addItem: (item: CartItem) => void;
}

export const createCartSlice = (set: SetState<CartSlice>): CartSlice => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
});

// store/index.ts
type StoreState = UserSlice & CartSlice;

export const useStore = create<StoreState>()((...a) => ({
  ...createUserSlice(...a),
  ...createCartSlice(...a),
}));
typescript
// store/slices/userSlice.ts
export interface UserSlice {
  user: User | null;
  setUser: (user: User) => void;
}

export const createUserSlice = (set: SetState<UserSlice>): UserSlice => ({
  user: null,
  setUser: (user) => set({ user }),
});

// store/slices/cartSlice.ts
export interface CartSlice {
  items: CartItem[];
  addItem: (item: CartItem) => void;
}

export const createCartSlice = (set: SetState<CartSlice>): CartSlice => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
});

// store/index.ts
type StoreState = UserSlice & CartSlice;

export const useStore = create<StoreState>()((...a) => ({
  ...createUserSlice(...a),
  ...createCartSlice(...a),
}));

7. Selectors with Shallow Compare

7. 浅比较选择器

typescript
import { shallow } from 'zustand/shallow';

// ✅ Prevents unnecessary re-renders
const { user, token } = useAuthStore(
  (state) => ({ user: state.user, token: state.token }),
  shallow
);
typescript
import { shallow } from 'zustand/shallow';

// ✅ 避免不必要的重渲染
const { user, token } = useAuthStore(
  (state) => ({ user: state.user, token: state.token }),
  shallow
);

8. Outside React (API calls, etc.)

8. React外部使用(API调用等)

typescript
// lib/api.ts
import { useAuthStore } from '@/store/useAuthStore';

export async function fetchWithAuth(url: string) {
  // ✅ Access store outside React
  const token = useAuthStore.getState().token;
  
  return fetch(url, {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });
}
typescript
// lib/api.ts
import { useAuthStore } from '@/store/useAuthStore';

export async function fetchWithAuth(url: string) {
  // ✅ 在React外部访问Store
  const token = useAuthStore.getState().token;
  
  return fetch(url, {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  });
}

Best Practices

最佳实践

DoDon't
✅ One store per domain❌ One giant store
✅ Select specific state❌ Select entire store
✅ Use persist for settings❌ Persist sensitive data
✅ Computed with
get()
❌ Store derived state
推荐做法不推荐做法
✅ 按领域拆分Store❌ 单一巨型Store
✅ 选择特定状态❌ 选择整个Store
✅ 对设置项使用持久化❌ 持久化敏感数据
✅ 使用
get()
实现计算属性
❌ 存储派生状态

References

参考资料