在 React 开发中,状态管理一直是个绕不开的话题。你是不是也经历过:用 useState
管理全局状态时,组件嵌套太深导致 props 层层传递("props 钻取");用 Redux 又觉得样板代码太多,配置繁琐;用 useContext + useReducer
吧,复杂场景下性能又跟不上?
今天给大家推荐一个 "宝藏级" 状态管理库 ------Zustand。它轻量、简洁,完全基于 hooks,几行代码就能实现全局状态管理,比 Redux 简单 10 倍,还没有 Context 的性能问题。
为什么需要 Zustand?现有方案的痛点
先说说我们熟悉的状态管理方案都有哪些问题:
useState + props
:适合组件内部状态,但全局状态需要层层传递,嵌套越深越麻烦("props 地狱");- Redux :功能强大但配置复杂,需要
store
、reducer
、action
、middleware
等,样板代码多,新手劝退; useContext + useReducer
:虽然比 Redux 简单,但 Context 会导致不必要的重渲染(只要 Context 变化,所有消费组件都会更新);- MobX:需要理解 "响应式" 概念,学习成本高,且写法不够 "React 原生"。
而 Zustand 完美解决了这些问题:
- 极简 API:用 hooks 定义和使用状态,几行代码搞定全局状态;
- 无 Provider 包裹 :不需要像 Context 那样用
Provider
包裹整个应用; - 精准更新:组件只订阅自己需要的状态,避免无关重渲染;
- 支持中间件:可以轻松集成持久化、日志等功能;
- 体积超小:核心代码只有 1KB 左右,几乎不增加项目体积。
Zustand 核心用法:3 步搞定全局状态
Zustand 的设计非常简洁,核心只有一个create
函数,用来创建 "状态仓库(store)",然后在组件中用自定义 hook 访问。
(1)安装依赖
bash
pnpm i zustand
(2)创建第一个 Store:计数器案例
先从最简单的计数器开始,感受 Zustand 的用法:
javascript
// store/count.js
import { create } from 'zustand';
// 用create创建store,返回一个自定义hook(通常命名为useXxxStore)
export const useCounterStore = create((set) => ({
// 1. 定义状态
count: 0,
// 2. 定义修改状态的方法(类似reducer,但更灵活)
increment: () => set((state) => ({ count: state.count + 1 })), // 累加
decrement: () => set((state) => ({ count: state.count - 1 })), // 递减
}));
核心说明:
create
函数接收一个回调,回调参数set
是一个函数,用于更新状态(类似setState
);set
可以接收一个函数((state) => newState
),通过旧状态计算新状态(避免状态依赖问题);- 返回的
useCounterStore
是一个自定义 hook,组件通过它访问状态和方法。
(3)在组件中使用 Store
不需要 Provider 包裹,直接在组件中调用useCounterStore
:
jsx
// components/Counter.jsx
import { useCounterStore } from '../store/count';
const Counter = () => {
// 从store中解构出状态和方法
const { count, increment, decrement } = useCounterStore();
return (
<div>
<p>计数器:{count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
};
甚至可以在多个组件中使用,状态会自动同步:
jsx
// App.jsx
import { useCounterStore } from './store/count';
import Counter from './components/Counter';
function App() {
// 在App组件中也能访问同一个count
const { count } = useCounterStore();
return (
<div>
<h1>App中的计数:{count}</h1>
<Counter /> {/* 这里的count和App中的count完全同步 */}
</div>
);
}
神奇之处:没有 Provider,没有 Context,直接调用 hook 就能共享状态,这就是 Zustand 的简洁所在。
进阶实战:用 Zustand 管理复杂状态
Zustand 不仅能处理简单计数器,复杂的列表、异步请求也能轻松应对,而且支持 "模块化拆分",让代码更清晰。
(1)管理 Todo 列表:增删改查全实现
javascript
// store/todos.js
import { create } from 'zustand';
export const useTodosStore = create((set) => ({
// 初始状态:一个Todo数组
todos: [
{ id: 1, text: '学习Zustand', completed: false },
{ id: 2, text: '写一篇博客', completed: false },
],
// 添加Todo
addTodo: (text) => set((state) => ({
todos: [
...state.todos,
{ id: state.todos.length + 1, text, completed: false },
],
})),
// 切换Todo完成状态
toggleTodo: (id) => set((state) => ({
todos: state.todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
),
})),
// 删除Todo
removeTodo: (id) => set((state) => ({
todos: state.todos.filter(todo => todo.id !== id),
})),
}));
在组件中使用:
jsx
// components/TodoList.jsx
import { useTodosStore } from '../store/todos';
import { useState } from 'react';
const TodoList = () => {
const [text, setText] = useState('');
const { todos, addTodo, toggleTodo, removeTodo } = useTodosStore();
return (
<div>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button onClick={() => { addTodo(text); setText(''); }}>
添加
</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>删除</button>
</li>
))}
</ul>
</div>
);
};

亮点:所有 Todo 操作逻辑集中在 store 中,组件只负责 UI 渲染,符合 "状态与 UI 分离" 的原则。
(2)处理异步请求:API 数据状态管理
实际项目中,经常需要管理 API 请求的 "加载中、数据、错误" 状态。Zustand 可以轻松封装这些逻辑:
步骤 1:封装 API 请求函数
javascript
// api/repo.js
import axios from './config'; // 配置了baseURL的axios实例
// 获取用户的仓库列表
export const getRepoList = async (username) => {
const res = await axios.get(`/users/${username}/repos`);
return res.data;
};
步骤 2:创建管理请求状态的 Store
javascript
// store/repos.js
import { create } from 'zustand';
import { getRepoList } from '../api/repo';
export const useRepoStore = create((set) => ({
repos: [], // 仓库列表数据
loading: false, // 加载状态
error: null, // 错误信息
// 异步获取仓库列表
fetchRepos: async (username = 'yourusername') => {
// 开始请求:设置loading为true
set({ loading: true, error: null });
try {
// 调用API
const data = await getRepoList(username);
// 请求成功:更新数据,关闭loading
set({ repos: data, loading: false });
} catch (err) {
// 请求失败:记录错误,关闭loading
set({ error: err.message, loading: false });
}
},
}));
步骤 3:在组件中使用异步状态
jsx
// components/RepoList.jsx
import { useEffect } from 'react';
import { useRepoStore } from '../store/repos';
const RepoList = () => {
const { repos, loading, error, fetchRepos } = useRepoStore();
// 组件挂载时请求数据
useEffect(() => {
fetchRepos();
}, [fetchRepos]);
// 处理加载和错误状态
if (loading) return <p>加载中...</p>;
if (error) return <p>出错了:{error}</p>;
return (
<div>
<h2>仓库列表</h2>
<ul>
{repos.map(repo => (
<li key={repo.id}>
<a href={repo.html_url} target="_blank" rel="noreferrer">
{repo.name}
</a>
<p>{repo.description || '无描述'}</p>
</li>
))}
</ul>
</div>
);
};
优势:把 "加载中、数据、错误" 的状态管理逻辑封装在 store 中,组件不用关心请求细节,只需渲染 UI。
(3)模块化:按功能拆分 Store
当项目变大时,建议按功能拆分 store(如count.js
、todos.js
、repos.js
),避免一个 store 过于庞大。
plaintext
src/
├── store/
│ ├── count.js // 计数器相关状态
│ ├── todos.js // Todo列表相关状态
│ └── repos.js // 仓库请求相关状态
└── components/ // 组件中按需引入对应的store
这种方式比 Redux 的 "单一 store + 多个 reducer" 更灵活,维护成本更低。
四、Zustand 为什么比其他方案更优?
对比主流状态管理方案,Zustand 的优势非常明显:
特性 | Zustand | Redux | useContext+useReducer |
---|---|---|---|
代码量 | 极少(无样板代码) | 多(action、reducer 等) | 较多(需创建 Context) |
学习成本 | 低(hooks 语法) | 高(概念多) | 中(需理解 Context) |
组件中使用方式 | 直接调用 hook | 需用 useSelector/useDispatch | 需用 useContext |
Provider 包裹 | 不需要 | 需要(Provider) | 需要(Context.Provider) |
适合场景 | 中小型项目、快速开发 | 大型项目、复杂状态逻辑 | 小型项目、简单跨组件共享 |
进阶技巧:这些功能让 Zustand 更强大
(1)状态持久化(刷新不丢失)
用zustand-persist
插件可以将状态保存到localStorage
或sessionStorage
,刷新页面后状态不丢失:
bash
pnpm i zustand-persist
javascript
// store/todos.js(持久化改造)
import { create } from 'zustand';
import { persist } from 'zustand/middleware'; // 引入持久化中间件
export const useTodosStore = create(
persist(
(set) => ({
todos: [],
addTodo: (text) => set((state) => ({
todos: [...state.todos, { id: Date.now(), text, completed: false }],
})),
// ...其他方法
}),
{
name: 'todos-storage', // localStorage的key
getStorage: () => localStorage, // 用localStorage存储
}
)
);
(2)选择状态(避免不必要的重渲染)
默认情况下,组件会在 store 中的任何状态变化时重新渲染。可以用 "选择器" 只订阅需要的状态,优化性能:
jsx
// 只订阅count,其他状态变化时不重渲染
const count = useCounterStore(state => state.count);
// 同时订阅多个状态(用对象或数组)
const { count, increment } = useCounterStore(state => ({
count: state.count,
increment: state.increment,
}));
总结:Zustand 让状态管理回归简单
Zustand 的设计理念是 "以最小的成本解决状态共享问题",它的核心优势是:
- 简洁 :用
create
函数创建 store,用 hook 访问,无需 Provider; - 灵活:支持同步状态、异步请求、模块化拆分;
- 高效:按需订阅状态,避免不必要的重渲染。
如果你受够了 Redux 的繁琐或 Context 的嵌套,试试 Zustand------ 它会让你发现,React 状态管理可以像用useState
一样自然,却能轻松搞定全局共享。