从 Redux 到 Zustand:状态管理的“极简主义”革命

165 阅读5分钟

Zustand 状态管理入门

用过 Redux 的小伙伴一定都深有体会:它的使用流程相对来说比较繁琐。首先需要定义一个个动作(action),然后编写一个又长又复杂的 reducer 函数来处理这些动作,接着在组件中通过 dispatch 主动触发这些动作,才能更新状态。而且,想要在组件中使用状态时,还得通过 connectuseSelector 一个个引入,使用起来不够直观。再加上 Redux 通常需要拆分出多个文件夹来管理 action、reducer、type、saga、store 等内容,整体文件结构显得非常复杂,对于新手或者中小型项目来说,学习和维护成本都比较高。为了解决这些问题,我们引入一种更轻量、更简单、更适合 React 项目的状态管理方案 —— Zustand

一、Zustand 是什么?为什么选择它?

Zustand 是一个轻量、灵活、易上手的状态管理库,专为 React 设计,目标是让状态管理变得简单又高效,非常适合希望快速开发、减少样板代码的 React 开发者。

🌟 Zustand 的核心优势:

特点描述
🧱 极简 API只需要几行代码就能创建 store
🔧 无需样板代码不需要写 actions、reducers、dispatch
✨ 状态直接更新就像操作普通对象一样方便
⚡ 自动优化渲染只在相关状态变化时更新组件
🧩 支持异步操作可以轻松处理 API 请求等异步逻辑

💡 小贴士:如果你觉得 Redux 太复杂、太繁琐,Zustand 是一个非常好的替代方案,尤其适合中小型项目。


二、项目初始化与安装

我们先创建一个 React 项目,并安装 Zustand。

1. 创建 React 项目(已有项目可跳过)

npx create-react-app zustand-demo
cd zustand-demo

2. 安装 Zustand

npm install zustand

三、创建 Zustand

(1)创建store

create() :是 Zustand 提供的核心函数,用于创建一个 store。useCounterStore中定义了多种更新数字的方法。

import create from 'zustand'
// 创建store(包含状态和方法)
const useCounterStore = create((set) => ({
  count: 0, // 初始状态
  name: 'Zustand Demo',
  
  // 定义更新方法
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  updateName: (newName) => set({ name: newName }),
  reset: () => set({ count: 0, name: 'Reset Done' })
}))

(2)更新state

  • 一次性拿到所有数据(比如 fullStore),但不推荐,因为这样组件会响应所有数据变化,效率低。
  • 只拿你需要的数据(比如 countname),这样组件只在这些数据变化时才更新,效率更高。
function Counter() {
  // 访问整个store(不推荐,会响应所有状态变化)
  const fullStore = useCounterStore()
  
  // 推荐:选择性订阅需要的状态/方法
  const { count, name } = useCounterStore(state => ({
    count: state.count,
    name: state.name
  }))
  const increment = useCounterStore(state => state.increment)
  const reset = useCounterStore(state => state.reset)

Zustand 的 store 中,状态和操作状态的方法是写在一起的,不需要像 Redux 那样拆分成多个文件。

💡 小贴士:Zustand 的 store 是一个React Hook,你可以直接在组件中调用它来获取状态和方法。


四、在组件中使用 Zustand

Zustand 的一大优点就是使用起来非常简单。我们只需要导入 store,就可以直接使用状态和方法。

✅ 示例 1:显示待办事项列表

使用步骤详解:
  1. 导入 store

    import useTodoStore from './store/todoStore'
    
  2. 使用 store 中的状态和方法

    const { todos, deleteTodo, toggleTodo } = useTodoStore()
    
  3. 渲染待办事项列表

    return (
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <span 
              style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
              onClick={() => toggleTodo(todo.id)}
            >
              {todo.text}
            </span>
            <button onClick={() => deleteTodo(todo.id)}>删除</button>
          </li>
        ))}
      </ul>
    )
    

🧠 小贴士:Zustand 使用小技巧

  • ✅ 状态和方法都在同一个 Hook 中:不需要像 Redux 那样分别导入 actions 和 selectors。
  • ✅ 组件只订阅使用到的状态:Zustand 会自动优化渲染,只有相关状态变化时才会更新组件。
  • ✅ 可以随时在任何地方访问状态:比如 useTodoStore.getState().todos
  • ✅ 支持异步操作:你可以在 store 中轻松处理 API 请求。

五、Zustand 的高级用法

在掌握了 Zustand 的基本使用之后,我们可以进一步了解它的一些高级用法,帮助我们更好地优化性能、扩展功能,让状态管理更加灵活高效。

✅ 1. 选择性订阅状态,提升组件性能

Zustand 的一个优势是:组件只会因它关心的状态变化而重新渲染,这可以有效提升性能。

我们可以通过传入一个选择器函数来只订阅我们关心的状态:

function TodoCounter() {
  const todoCount = useTodoStore(state => state.todos.length)
  return <div>总待办事项: {todoCount}</div>
}

上面这个组件只关心 todos 的长度,所以只有当 todos 发生变化时,这个组件才会重新渲染,而不是每次整个 store 更新都重新渲染。

🧠 小贴士:如果直接使用 const store = useTodoStore(),组件会在 store 中任何状态变化时都重新渲染,效率较低。


✅ 2. 在组件外部访问和修改状态

Zustand 不仅能在 React 组件中使用,还可以在非组件文件中访问和修改状态,比如在工具函数、服务文件中使用。

// 获取当前状态
const currentTodos = useTodoStore.getState().todos

// 修改状态
useTodoStore.setState({ todos: [] })

这在处理异步逻辑、封装业务逻辑时非常有用。比如你可以写一个 todoService.js 文件来统一管理所有的 todo 操作。


✅ 3. 支持异步操作,轻松处理网络请求

Zustand 并不排斥异步操作,你可以在 store 中直接定义异步方法,比如请求数据、保存数据等:

const useTodoStore = create((set) => ({
  todos: [],
  loading: false,

  fetchTodos: async () => {
    set({ loading: true })
    const response = await fetch('/api/todos')
    const todos = await response.json()
    set({ todos, loading: false })
  }
}))

然后你可以在组件中这样调用:

function TodoList() {
  const { fetchTodos, todos, loading } = useTodoStore()

  useEffect(() => {
    fetchTodos()
  }, [])

  if (loading) return <div>加载中...</div>

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  )
}

这样,数据请求和状态更新就统一管理在 store 中,逻辑清晰,便于维护。


✅ 4. 使用中间件扩展功能(可选)

虽然这不属于本节重点,但值得一提的是,Zustand 支持中间件,比如:

  • zustand/middleware:支持日志、持久化、时间旅行调试等
  • zustand/devtools:支持 Redux DevTools 调试

你可以像这样开启 DevTools 支持:

import { devtools } from 'zustand/middleware'

const useStore = create(devtools((set) => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 }))
})))