为什么需要状态管理?
在现代前端开发中,随着应用复杂度增加,组件状态共享和状态逻辑复用成为核心挑战。传统的useContext + useReducer组合虽然有效,但随着项目规模增大,会遇到以下问题:
- 上下文嵌套过深(Provider Hell)
- 不必要的组件重渲染
- 状态逻辑分散难以维护
Zustand应运而生——一个轻量级(仅1.6KB)但功能强大的状态管理库,完美融合了React Hooks的简洁性和Redux的可预测性。
count响应式状态实现全局管理
通常情况下咱们会使用下面的Hooks进行状态管理,但是这样太复杂
useContext + useReducer + React.createContext
咱们可以使用redux/zustand进行简化,今天咱们就来使用下zustand该轻量级状态管理库实现咱们的项目。
count响应式实现
1.项目结构组织
src/
├── store/
│ ├── index.js # 聚合所有store
│ ├── count.js # 计数器store
│ ├── todos.js # 待办事项store
│ └── repos.js # 仓库store
├── api/
│ ├── config.js # API配置
│ └── repo.js # 仓库API
└── components/
├── Counter/
├── TodoList/
└── RepoList/
2. store仓库
下面让咱们先安装依赖
// zustand react 状态管理框架
pnpm i zustand
现在需要create从zustand当中引入zustand状态管理,并且在该状态下创建一个useCounterStore钩子函数。
// zustand react 状态管理框架
import {
create // 创建一个store 存状态的地方
} from 'zustand';
export const useCounterStore = create(() => ({
// 对象
}))
在上述代码当中,create当中是括号可以知道在仓库存储的应该为一个对象,因此返回的输出应为一个对象,原来的话,咱们需要将修改count的方法写在reducer当中。但现在咱们就可以直接在自己写的hook函数当中定义方法就可以。
// zustand react 状态管理框架
import {
create // 创建一个store 存状态的地方
} from 'zustand';
export const useCounterStore = create((set) => ({
// 对象
// 状态
// 修改状态的方法
count: 0,
// reducer 函数 规定状态怎么变
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}))
3.components
首先我们需要明白如果是小型项目的话,有很多地方都可以省略,因为小型项目当中,咱们可能就只是写一个页面。并没有其它的页面可以选择,因此componnents和router可以不用配置。接下来咱们来到components组件下,将咱们需要的count响应式管理呈现在页面当中。
import { useCounterStore } from "../../store/count"
// 来自store
const Counter = () => {
const {
count,
increment,
decrement
} = useCounterStore()
return (
<>
Counter {count}
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</>
)
}
export default Counter
关键点解析:
create函数创建store,返回一个Hookset函数用于更新状态(类似React的setState)- 组件直接使用Hook获取状态和操作,无需Provider包裹
在这里可以看到对比之前使用的Reducer当中,这里的代码使用就简洁了许多,并不需要使用到额外的store库来书写。可以自定义更简洁的函数来代替Reducer当中的action和dispatch,咱们可以拿之前的reducer当中的代码进行对比:
// 状态的正确
// reducers文件中的 todoReducers.js文件当中
function todoReducers(state, action){
switch(action.type){
case 'ADD_TODO':
return [...state,{
id: Date.now(),
text: action.text,
done: false
}];
case 'TOGGLE_TODO':
return state.map(todo =>
todo.id === action.id ?
{...todo, done: !todo.done} : todo
);
case 'REMOVE_TODO':
return state.filter(todo => todo.id !== action.id);
default:
return state;
}
}
export default todoReducers;
// hooks文件下的 useTodos.js文件中
import {
useReducer
}from 'react';
import todoReducers from '../reducers/todoReducers';
// 参数的默认值
// {todos, } key:value 省略
// `` 模板字符串
// 解构 [] = [] {} = {}
// 展开运算符, ... rest 运算符
export function useTodos(initial = []){
const [todos, dispatch] = useReducer(todoReducers, initial);
const addTodo = text => dispatch({type: 'ADD_TODO', text})
const toggleTodo = id => dispatch({type: 'TOGGLE_TODO', id})
const removeTodo = id => dispatch({type: 'REMOVE_TODO',id})
return {
todos,
addTodo,
toggleTodo,
removeTodo
}
}
| 特性 | Zustand | useReducer + useContext |
|---|---|---|
| 是否官方支持 | ❌ 第三方库 | ✅ 官方支持 |
| 是否适合全局状态管理 | ✅ 简单方便 | ✅ 可以,但需要 Context |
| 学习成本 | ⭐ 简单,易于上手 | ⭐⭐ 相对复杂 |
| 性能表现 | ✅ 高性能,细粒度更新 | ✅ 可优化,但需注意 Context 重渲染 |
| 代码量 | ⭐ 代码少,结构清晰 | ⭐⭐ 代码较多 |
| 可测试性 | ⭐ 状态和方法集中,便于测试 | ⭐ reducer 是纯函数,可测试性强 |
| 中间件/扩展能力 | ✅ 支持插件(如持久化、日志) | ❌ 需要手动实现 |
| 适合场景 | ✅ 中小型项目、快速开发 | ✅ 中大型项目、复杂状态逻辑 |
实战:构建Zustand全家桶应用
1. TodoList应用开发
store/todos.js
import { create } from 'zustand';
export const useTodosStore = create((set) => ({
todos: [],
// 添加待办事项
addTodo: (text) => set((state) => ({
todos: [
...state.todos,
{
id: Date.now(), // 使用时间戳作为ID
text,
completed: false
}
]
})),
// 切换完成状态
toggleTodo: (id) => set((state) => ({
todos: state.todos.map(todo =>
todo.id === id ? {...todo, completed: !todo.completed} : todo
)
})),
// 删除待办事项
deleteTodo: (id) => set((state) => ({
todos: state.todos.filter(todo => todo.id !== id)
}))
}));
components/TodoList.jsx
import { useTodosStore } from "../store/todos";
const TodoList = () => {
const { todos, addTodo, toggleTodo, deleteTodo } = useTodosStore();
return (
<div className="todo-container">
<ul>
{todos.map(todo => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
<span>{todo.text}</span>
<div>
<button onClick={() => toggleTodo(todo.id)}>
{todo.completed ? '撤销' : '完成'}
</button>
<button onClick={() => deleteTodo(todo.id)}>删除</button>
</div>
</li>
))}
</ul>
<input
type="text"
placeholder="添加新任务..."
onKeyDown={(e) => {
if (e.key === 'Enter' && e.target.value.trim()) {
addTodo(e.target.value.trim());
e.target.value = '';
}
}}
/>
</div>
);
};
设计亮点:
- 状态与UI分离:所有业务逻辑封装在store中
- 不可变更新:通过扩展运算符确保状态不可变
- 自然的事务处理:每个操作都是原子性的
2. 异步数据获取(GitHub仓库列表)
api/config.js
// 配置文件
import axios from 'axios'
axios.defaults.baseURL = "http://api.github.com"
export default axios
api/repo.js
import axios from './config';
// 配置基础URL
export const getRepos = async (owner, repo) =>
await axios.get(`/repos/${owner}/${repo}`)
export const getRepoList = async (owner) =>
await axios.get(`/users/${owner}/repos`)
store/repos.js
import { create } from 'zustand';
import { getRepoList } from '../api/repo';
export const useReposStore = create((set) => ({
repos: [],
loading: false,
error: null,
// 异步获取仓库数据
fetchRepos: async (username) => {
set({ loading: true, error: null });
try {
const repos = await getRepoList(username);
set({ repos, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
}));
components/RepoList.jsx
import {
useReposStore
} from '../../store/repos'
import { useEffect } from 'react'
const RepoList = () => {
const { repos, loading, fetchRepos, error } = useReposStore()
useEffect(() => {
fetchRepos()
}, [])
if (loading) return <p>loading...</p>
if (error) return <p>{error}</p>
return (
<div>
<h2>Repo List</h2>
<ul>
{
repos.map(repo => (
<li key={repo.id}>
<a href={repo.html_url} target="_blank" rel="noreferrer">
{repo.name}
</a>
<p>{repo.description || 'No description'}</p>
</li>
))
}
{
error && <li>{error}</li>
}
</ul>
</div>
)
}
export default RepoList
异步处理最佳实践:
- 状态追踪:设置loading和error状态
- 错误边界:捕获并处理可能的异常
- 用户体验:提供加载状态和错误反馈
- 数据转换:在store中处理数据格式化
可以看到最终咱们实现的页面效果图啦:
总结
graph TD
A[组件] --> B[Zustand Store]
B --> C[计数器状态]
B --> D[待办事项状态]
B --> E[API数据状态]
C --> F[计数器组件]
D --> G[待办列表组件]
E --> H[仓库列表组件]
Zustand代表了React状态管理的新范式——它平衡了简单性和功能性,让开发者可以专注于业务逻辑而非状态管理框架本身。无论是小型工具还是大型应用,Zustand都能提供优雅的解决方案。