Skip to content

Zustand 状态管理

在大型 React 项目中,使用 Zustand 管理全局状态是一种轻量、灵活且性能优秀的方案。以下是以一个 Todo List(待办事项)系统 为例,详细说明如何在大型项目中使用 Zustand 管理全局状态及操作方法。


🧩 一、状态设计

以 Todo List 为例,全局状态包括:

  • 所有待办项 todos
  • 当前筛选条件 filter(如 all / completed / active)
  • 异步加载状态 loading
  • 错误信息 error(用于接口错误提示)

📦 二、安装 Zustand

bash
pnpm add zustand

🏗️ 三、构建 Store(store/todoStore.ts)

ts
import { create } from 'zustand'

export type Todo = {
  id: string
  title: string
  completed: boolean
}

type Filter = 'all' | 'completed' | 'active'

interface TodoStore {
  todos: Todo[]
  filter: Filter
  loading: boolean
  error: string | null

  // actions
  fetchTodos: () => Promise<void>
  addTodo: (title: string) => void
  toggleTodo: (id: string) => void
  removeTodo: (id: string) => void
  setFilter: (filter: Filter) => void
}

export const useTodoStore = create<TodoStore>((set, get) => ({
  todos: [],
  filter: 'all',
  loading: false,
  error: null,

  fetchTodos: async () => {
    set({ loading: true, error: null })
    try {
      // 假设接口请求
      const response = await fetch('/api/todos')
      const data: Todo[] = await response.json()
      set({ todos: data })
    } catch (err: any) {
      set({ error: err.message || 'Failed to fetch todos' })
    } finally {
      set({ loading: false })
    }
  },

  addTodo: (title) => {
    const newTodo: Todo = {
      id: crypto.randomUUID(),
      title,
      completed: false
    }
    set({ todos: [...get().todos, newTodo] })
  },

  toggleTodo: (id) => {
    set({
      todos: get().todos.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    })
  },

  removeTodo: (id) => {
    set({ todos: get().todos.filter(todo => todo.id !== id) })
  },

  setFilter: (filter) => set({ filter })
}))

🧪 四、使用状态和操作

tsx
import { useTodoStore } from '@/store/todoStore'

function TodoList() {
  const { todos, filter, toggleTodo, removeTodo } = useTodoStore()
  
  const filteredTodos = todos.filter(todo => {
    if (filter === 'completed') return todo.completed
    if (filter === 'active') return !todo.completed
    return true
  })

  return (
    <ul>
      {filteredTodos.map(todo => (
        <li key={todo.id}>
          <input type="checkbox" checked={todo.completed} onChange={() => toggleTodo(todo.id)} />
          {todo.title}
          <button onClick={() => removeTodo(todo.id)}>删除</button>
        </li>
      ))}
    </ul>
  )
}

⚙️ 五、Filter 设置与新增 Todo

tsx
function TodoControls() {
  const { filter, setFilter, addTodo } = useTodoStore()
  const [input, setInput] = useState('')

  return (
    <div>
      <input value={input} onChange={e => setInput(e.target.value)} />
      <button onClick={() => { addTodo(input); setInput('') }}>添加</button>
      <select value={filter} onChange={e => setFilter(e.target.value as any)}>
        <option value="all">全部</option>
        <option value="active">未完成</option>
        <option value="completed">已完成</option>
      </select>
    </div>
  )
}

🔍 六、项目结构建议(中大型项目)

src/
├── store/
│   └── todoStore.ts      # Zustand store
├── components/
│   ├── TodoList.tsx
│   └── TodoControls.tsx
├── pages/
│   └── Home.tsx

✅ 七、进阶建议

  1. Middleware 使用

    • 如 Zustand 的 persist 用于本地持久化,devtools 配合 Redux DevTools 调试。
  2. Selectors 优化

    • 减少组件不必要的重新渲染,例如:

      ts
      const todos = useTodoStore(state => state.todos)
  3. 组合多个 Store

    • 拆分不同模块(如 AuthStore、TodoStore),每个模块一个 store 文件。
  4. 异步操作封装

    • 可将异步 API 封装到 services/ 中,在 store 内调用,保持 store 的纯粹性。

🧠 总结

使用 Zustand 管理 Todo List 全局状态的优势:

优点表现
简洁轻量无需引入 reducer / action 概念
无 Provider组件中直接使用,无需 Context 包裹
高性能支持选择性订阅,避免不必要更新
可拓展支持中间件、组合多个 store