在 React 开发中,状态管理是一个核心问题。从 Context API 到 Redux,开发者们一直在寻找更简洁、更高效的状态管理方案。今天,我们来介绍一款轻量级但功能强大的状态管理库------Zustand。
1. 概括
Zustand 是由 Poimandres 团队开发的一款 React 状态管理库,它以简洁的 API、灵活的使用方式和优秀的性能而备受青睐。与 Redux 等重型状态管理库相比,Zustand 更加轻量,学习曲线平缓,同时保持了强大的功能。
Zustand 的优势:
- 简洁的 API:无需繁琐的样板代码,几行代码即可创建一个 store
- 无需 Provider 包裹:直接使用,减少组件层级嵌套
- 灵活的状态订阅:组件可以精确订阅需要的状态,避免不必要的重渲染
- 支持中间件:如 Redux 开发者工具、持久化等
- 良好的 TypeScript 支持:提供完整的类型定义
2. 快速开始
首先,我们需要安装 Zustand:
bash
npm install zustand
# 或
yarn add zustand
2.1. 基本用法
创建一个 store 非常简单,只需调用 create
函数并传入一个定义状态和操作的函数:
javascript
import { create } from 'zustand';
// 创建一个计数器 store
const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));
在组件中使用这个 store:
jsx
function Counter() {
// 只订阅我们需要的状态和方法
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>+</button>
</div>
);
}
3. 核心概念
下面是Zustand的核心概念:
3.1. Store
Store 是状态的容器,包含了应用的状态和修改状态的方法。使用 create
函数创建,返回一个自定义钩子(如 useCounterStore
)。
3.2. 状态更新
使用 set
函数来更新状态,它可以接收一个新的状态对象或一个返回新状态的函数:
javascript
// 直接设置新状态
set({ count: 10 });
// 基于当前状态计算新状态
set((state) => ({ count: state.count + 1 }));
3.3. 状态订阅
组件通过调用 store 钩子并传入选择器函数来订阅状态:
javascript
// 订阅单个状态
const count = useCounterStore((state) => state.count);
// 订阅多个状态
const { count, user } = useCounterStore((state) => ({
count: state.count,
user: state.user
}));
注意:当订阅多个状态时,如果其中任何一个状态发生变化,组件都会重新渲染。
4. 进阶用法
下面使用一些进阶的用法:
4.1. 异步操作
Zustand 对异步操作有原生支持,直接在 action 中使用 async/await 即可:
javascript
const useUserStore = create((set) => ({
user: null,
loading: false,
error: null,
fetchUser: async (userId) => {
set({ loading: true, error: null });
try {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
set({ user, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
}));
4.2. 结合 Context
虽然 Zustand 不需要 Context,但在某些情况下(如需要在非 React 组件中访问状态),可以结合 Context 使用:
javascript
import { createContext, useContext } from 'react';
import { create } from 'zustand';
import { createContextStore } from 'zustand/context';
// 创建一个 context
const MyContext = createContext();
// 创建 store 和 Provider
const { Provider, useStore } = createContextStore(() => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));
// 在组件树中使用 Provider
function App() {
return (
<Provider>
<ChildComponent />
</Provider>
);
}
// 在子组件中使用
function ChildComponent() {
const count = useStore((state) => state.count);
// ...
}
4.3. 中间件
Zustand 支持多种中间件,扩展其功能:
4.3.1. Redux 开发者工具
javascript
import { devtools } from 'zustand/middleware';
const useStore = create(
devtools((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
);
4.3.2. 持久化
javascript
import { persist } from 'zustand/middleware';
const useStore = create(
persist(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}),
{
name: 'count-storage', // 存储的键名
getStorage: () => localStorage, // 使用 localStorage
}
)
);
5. 实战示例:待办事项应用
让我们用 Zustand 实现一个简单的待办事项应用:
javascript
// stores/useTodoStore.js
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
export const useTodoStore = create(
devtools(
persist(
(set, get) => ({
todos: [],
addTodo: (text) => {
const newTodo = {
id: Date.now(),
text,
completed: false,
};
set((state) => ({ todos: [...state.todos, newTodo] }));
},
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),
}));
},
clearCompleted: () => {
set((state) => ({
todos: state.todos.filter((todo) => !todo.completed),
}));
},
}),
{
name: 'todo-storage',
}
)
)
);
在组件中使用:
jsx
// TodoApp.jsx
import { useTodoStore } from './stores/useTodoStore';
import { useState } from 'react';
function TodoApp() {
const [text, setText] = useState('');
const { todos, addTodo, toggleTodo, deleteTodo, clearCompleted } = useTodoStore();
const handleSubmit = (e) => {
e.preventDefault();
if (!text.trim()) return;
addTodo(text);
setText('');
};
return (
<div>
<h1>Todo List</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Add a new todo"
/>
<button type="submit">Add</button>
</form>
<ul>
{todos.map((todo) => (
<li
key={todo.id}
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
>
<span onClick={() => toggleTodo(todo.id)}>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
{todos.some(todo => todo.completed) && (
<button onClick={clearCompleted}>Clear Completed</button>
)}
</div>
);
}
export default TodoApp;
6. 总结
Zustand 为 React 应用提供了一种简单、灵活且高效的状态管理方案。它摒弃了繁琐的样板代码,让开发者能够更专注于业务逻辑的实现。无论是小型应用还是大型项目,Zustand 都能很好地满足需求。其轻量级的特性使其成为许多场景下的理想选择,特别是当你觉得 Redux 过于复杂,而 Context API 又不够用的时候。
如果你还在寻找适合自己项目的状态管理方案,不妨试试 Zustand,相信它会给你带来惊喜。
参考资料
本次分享就到这儿啦,我是鹏多多,如果看了觉得有帮助的,欢迎 点赞 关注 评论,在此谢过道友;
往期文章