Zustand 是一个轻量级、简洁的React状态管理库,核心特点是无样板代码、hooks风格、不依赖Context。用法很像Vue生态中的Pinia,以下是详细使用指南:
一、基础使用(计数器示例)
1. 创建store
typescript
// store/counterStore.ts
import { create } from 'zustand'
interface CounterState {
count: number
increment: () => void
decrement: () => void
reset: () => void
}
const useCounterStore = create<CounterState>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}))
2. 组件中使用
javascript
// Counter.tsx
import useCounterStore from './store/counterStore'
const Counter = () => {
// 自动订阅状态变化 - 当count变化时组件重渲染
const { count, increment, decrement } = useCounterStore()
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
)
}
// 兄弟组件同步更新
const Display = () => {
const count = useCounterStore((state) => state.count) // 选择性订阅
return <div>Current count: {count}</div>
}
二、进阶特性
1. 异步操作
typescript
// store/userStore.ts
interface UserState {
user: User | null
loading: boolean
fetchUser: (id: string) => Promise<void>
}
const useUserStore = create<UserState>((set) => ({
user: null,
loading: false,
fetchUser: async (id: string) => {
set({ loading: true })
try {
const response = await fetch(`/api/users/${id}`)
const user = await response.json()
set({ user, loading: false })
} catch (error) {
set({ loading: false })
throw error
}
},
}))
2. 深层嵌套状态更新
typescript
// 使用immer简化嵌套更新(需安装immer)
import { produce } from 'immer'
const useTodoStore = create<{
todos: Todo[]
addTodo: (text: string) => void
toggleTodo: (id: number) => void
}>((set) => ({
todos: [],
addTodo: (text) => set(produce((state) => {
state.todos.push({ id: Date.now(), text, completed: false })
})),
toggleTodo: (id) => set(produce((state) => {
const todo = state.todos.find(t => t.id === id)
if (todo) todo.completed = !todo.completed
})),
}))
3. 性能优化:选择性订阅
javascript
// ✅ 只订阅需要的状态,避免不必要的重渲染
const TodoList = () => {
const todos = useTodoStore((state) => state.todos) // 仅当todos变化时重渲染
const addTodo = useTodoStore((state) => state.addTodo) // action不会触发重渲染
return (
<div>
{todos.map(todo => <TodoItem key={todo.id} />)}
<AddTodo onAdd={addTodo} />
</div>
)
}
// TodoItem组件 - 独立订阅
const TodoItem = ({ id }) => {
const todo = useTodoStore(
(state) => state.todos.find(t => t.id === id)
)
const toggleTodo = useTodoStore((state) => state.toggleTodo)
// 每个TodoItem只在自己的todo变化时重渲染
return <li onClick={() => toggleTodo(id)}>{todo.text}</li>
}
4. 中间件使用
javascript
// 持久化存储(需安装zustand/middleware)
import { persist, createJSONStorage } from 'zustand/middleware'
const useAuthStore = create(
persist(
(set, get) => ({
token: null,
user: null,
login: (credentials) => { /* ... */ },
logout: () => set({ token: null, user: null }),
}),
{
name: 'auth-storage', // localStorage key
storage: createJSONStorage(() => localStorage),
}
)
)
// 开发工具中间件
import { devtools } from 'zustand/middleware'
const useStore = create(
devtools(
(set) => ({
/* state & actions */
}),
{ name: 'MyStore' } // Redux DevTools中的显示名称
)
)
三、最佳实践
1. 拆分store(按领域划分)
bash
store/
├── authStore.ts # 认证相关
├── cartStore.ts # 购物车
├── uiStore.ts # UI状态(主题、弹窗)
└── productStore.ts # 商品数据
2. TypeScript完整示例
typescript
// store/authStore.ts
interface User {
id: string
name: string
email: string
}
interface AuthState {
user: User | null
isAuthenticated: boolean
login: (email: string, password: string) => Promise<void>
logout: () => void
initialize: () => Promise<void>
}
export const useAuthStore = create<AuthState>((set, get) => ({
user: null,
isAuthenticated: false,
login: async (email: string, password: string) => {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
})
const user = await response.json()
set({ user, isAuthenticated: true })
},
logout: () => {
localStorage.removeItem('token')
set({ user: null, isAuthenticated: false })
},
initialize: async () => {
const token = localStorage.getItem('token')
if (token) {
// 验证token并获取用户信息
const user = await fetchCurrentUser()
set({ user, isAuthenticated: true })
}
},
}))
3. 在类组件中使用(兼容方案)
javascript
// withStore HOC
import { useCounterStore } from './store/counterStore'
const withCounterStore = (Component) => {
return (props) => {
const store = useCounterStore()
return <Component {...props} store={store} />
}
}
class LegacyComponent extends React.Component {
render() {
const { store } = this.props
return <div>Count: {store.count}</div>
}
}
export default withCounterStore(LegacyComponent)
四、对比其他方案
| 特性 | Zustand | Redux Toolkit | Context |
|---|---|---|---|
| 包大小 | ~1KB | ~8KB | 内置 |
| 学习曲线 | 极低 | 中等 | 低 |
| 样板代码 | 极少 | 中等 | 多 |
| 性能 | 自动优化 | 手动优化 | 需要Memo |
| DevTools | 插件支持 | 内置完整 | 无 |
| 异步 | 原生支持 | RTK Query | 需手动处理 |
五、与Pinia的开发体验对比
| 方面 | Zustand | Pinia | 评价 |
|---|---|---|---|
| 学习成本 | 极低 | 低 | Zustand更简单 |
| 代码简洁度 | 极高 | 高 | Zustand更函数式 |
| Vue集成 | 不适用 | 完美 | Pinia是Vue生态一部分 |
| React集成 | 原生hooks | 不适用 | Zustand是React专用 |
| 调试体验 | 需要中间件 | 开箱即用 | Pinia胜出 |
| 社区生态 | 增长快 | Vue官方 | Pinia更稳定 |
六、快速开始
bash
# 安装
npm install zustand
# 可选中间件
npm install immer @types/immer # 不可变更新
npm install zustand/middleware # 官方中间件
总结 :Zustand通过极简的API提供了完整的全局状态管理能力,适合大多数React项目。其核心优势是零样板、直观、高性能,避免了Redux的复杂性和Context的性能陷阱。