name: zustand-store-patterns user-invocable: false description: 用于创建和管理React状态管理的Zustand存储。涵盖存储创建、选择器、动作和基本使用模式。 allowed-tools:
- Read
- Write
- Edit
- Bash
- Grep
- Glob
Zustand - 存储模式
Zustand是一个小型、快速且可扩展的React状态管理解决方案。它使用简化的flux原则和基于hooks的API。
关键概念
存储创建
Zustand存储使用create函数创建:
import { create } from 'zustand'
interface BearStore {
bears: number
increasePopulation: () => void
removeAllBears: () => void
}
const useBearStore = create<BearStore>((set) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
removeAllBears: () => set({ bears: 0 }),
}))
在组件中使用存储
function BearCounter() {
const bears = useBearStore((state) => state.bears)
return <h1>{bears} around here...</h1>
}
function Controls() {
const increasePopulation = useBearStore((state) => state.increasePopulation)
return <button onClick={increasePopulation}>Add bear</button>
}
状态更新
Zustand提供两种更新状态的方式:
// 替换状态
set({ bears: 5 })
// 合并状态(浅合并)
set((state) => ({ bears: state.bears + 1 }))
最佳实践
1. 使用选择器以提高性能
只选择需要的状态以防止不必要的重新渲染:
// ❌ 坏:组件在任何状态变化时重新渲染
function BadComponent() {
const store = useBearStore()
return <div>{store.bears}</div>
}
// ✅ 好:组件只在bears变化时重新渲染
function GoodComponent() {
const bears = useBearStore((state) => state.bears)
return <div>{bears}</div>
}
2. 将动作与状态分离
通过分离数据和动作来组织存储:
interface TodoStore {
// 状态
todos: Todo[]
filter: 'all' | 'active' | 'completed'
// 动作
addTodo: (text: string) => void
toggleTodo: (id: string) => void
removeTodo: (id: string) => void
setFilter: (filter: TodoStore['filter']) => void
}
const useTodoStore = create<TodoStore>((set) => ({
todos: [],
filter: 'all',
addTodo: (text) =>
set((state) => ({
todos: [...state.todos, { id: Date.now().toString(), text, completed: false }],
})),
toggleTodo: (id) =>
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
),
})),
removeTodo: (id) =>
set((state) => ({
todos: state.todos.filter((todo) => todo.id !== id),
})),
setFilter: (filter) => set({ filter }),
}))
3. 使用浅相等进行多重选择
当选择多个值时,使用来自zustand/shallow的shallow:
import { create } from 'zustand'
import { shallow } from 'zustand/shallow'
const useStore = create<Store>((set) => ({
nuts: 0,
honey: 0,
increaseNuts: () => set((state) => ({ nuts: state.nuts + 1 })),
increaseHoney: () => set((state) => ({ honey: state.honey + 1 })),
}))
// ✅ 使用浅比较
function Component() {
const { nuts, honey } = useStore(
(state) => ({ nuts: state.nuts, honey: state.honey }),
shallow
)
return <div>{nuts} nuts, {honey} honey</div>
}
4. 使用切片组织大型存储
对于复杂应用,将存储拆分为逻辑切片:
interface UserSlice {
user: User | null
login: (credentials: Credentials) => Promise<void>
logout: () => void
}
interface CartSlice {
items: CartItem[]
addItem: (item: Product) => void
removeItem: (id: string) => void
clearCart: () => void
}
const createUserSlice = (set: StateCreator<UserSlice>) => ({
user: null,
login: async (credentials) => {
const user = await api.login(credentials)
set({ user })
},
logout: () => set({ user: null }),
})
const createCartSlice = (set: StateCreator<CartSlice>) => ({
items: [],
addItem: (product) =>
set((state) => ({
items: [...state.items, { ...product, quantity: 1 }],
})),
removeItem: (id) =>
set((state) => ({
items: state.items.filter((item) => item.id !== id),
})),
clearCart: () => set({ items: [] }),
})
const useStore = create<UserSlice & CartSlice>()((...a) => ({
...createUserSlice(...a),
...createCartSlice(...a),
}))
5. 在组件外访问存储
使用getState和setState进行非响应式访问:
const useBearStore = create<BearStore>((set, get) => ({
bears: 0,
increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
doSomething: () => {
const currentBears = get().bears
console.log(`Current bears: ${currentBears}`)
},
}))
// 在组件外
const currentState = useBearStore.getState()
useBearStore.setState({ bears: 10 })
示例
简单计数器存储
import { create } from 'zustand'
interface CounterStore {
count: number
increment: () => void
decrement: () => void
reset: () => void
}
export const useCounterStore = create<CounterStore>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}))
// 用法
function Counter() {
const { count, increment, decrement, reset } = useCounterStore()
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
)
}
购物车存储
import { create } from 'zustand'
interface CartItem {
id: string
name: string
price: number
quantity: number
}
interface CartStore {
items: CartItem[]
addItem: (product: Omit<CartItem, 'quantity'>) => void
removeItem: (id: string) => void
updateQuantity: (id: string, quantity: number) => void
clearCart: () => void
total: number
}
export const useCartStore = create<CartStore>((set, get) => ({
items: [],
addItem: (product) =>
set((state) => {
const existingItem = state.items.find((item) => item.id === product.id)
if (existingItem) {
return {
items: state.items.map((item) =>
item.id === product.id
? { ...item, quantity: item.quantity + 1 }
: item
),
}
}
return {
items: [...state.items, { ...product, quantity: 1 }],
}
}),
removeItem: (id) =>
set((state) => ({
items: state.items.filter((item) => item.id !== id),
})),
updateQuantity: (id, quantity) =>
set((state) => ({
items: state.items.map((item) =>
item.id === id ? { ...item, quantity } : item
),
})),
clearCart: () => set({ items: [] }),
get total() {
return get().items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
)
},
}))
认证存储
import { create } from 'zustand'
interface User {
id: string
email: string
name: string
}
interface AuthStore {
user: User | null
token: string | null
isLoading: boolean
error: string | null
login: (email: string, password: string) => Promise<void>
logout: () => void
checkAuth: () => Promise<void>
}
export const useAuthStore = create<AuthStore>((set) => ({
user: null,
token: null,
isLoading: false,
error: null,
login: async (email, password) => {
set({ isLoading: true, error: null })
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
})
if (!response.ok) {
throw new Error('Login failed')
}
const { user, token } = await response.json()
set({ user, token, isLoading: false })
localStorage.setItem('token', token)
} catch (error) {
set({
error: error instanceof Error ? error.message : 'Login failed',
isLoading: false,
})
}
},
logout: () => {
localStorage.removeItem('token')
set({ user: null, token: null })
},
checkAuth: async () => {
const token = localStorage.getItem('token')
if (!token) return
set({ isLoading: true })
try {
const response = await fetch('/api/me', {
headers: { Authorization: `Bearer ${token}` },
})
if (!response.ok) {
throw new Error('Auth check failed')
}
const user = await response.json()
set({ user, token, isLoading: false })
} catch (error) {
localStorage.removeItem('token')
set({ user: null, token: null, isLoading: false })
}
},
}))
常见模式
计算值
使用getter获取派生状态:
const useStore = create<Store>((set, get) => ({
items: [],
get itemCount() {
return get().items.length
},
get hasItems() {
return get().items.length > 0
},
}))
异步动作
在动作内处理异步操作:
const useStore = create<Store>((set) => ({
data: null,
isLoading: false,
error: null,
fetchData: async () => {
set({ isLoading: true, error: null })
try {
const data = await api.fetchData()
set({ data, isLoading: false })
} catch (error) {
set({ error: error.message, isLoading: false })
}
},
}))
重置存储
实现重置动作:
const initialState = {
count: 0,
name: '',
}
const useStore = create<Store>((set) => ({
...initialState,
increment: () => set((state) => ({ count: state.count + 1 })),
setName: (name: string) => set({ name }),
reset: () => set(initialState),
}))
反模式
❌ 不要直接修改状态
// 坏
const useStore = create((set) => ({
items: [],
addItem: (item) => {
// 不要直接修改状态
items.push(item)
},
}))
// 好
const useStore = create((set) => ({
items: [],
addItem: (item) =>
set((state) => ({
items: [...state.items, item],
})),
}))
❌ 不要选择整个存储
// 坏:在任何状态变化时导致重新渲染
const store = useStore()
// 好:只选择需要的部分
const count = useStore((state) => state.count)
❌ 不要在选择器中使用外部状态
// 坏:选择器依赖外部值
const [userId, setUserId] = useState('123')
const user = useStore((state) => state.users[userId])
// 好:将外部值作为参数传递
const getUser = (userId: string) => useStore.getState().users[userId]
❌ 不要为相关数据创建多个存储
// 坏:将相关状态拆分到多个存储
const useUserStore = create(...)
const useUserSettingsStore = create(...)
const useUserPreferencesStore = create(...)
// 好:将相关状态保持在一起
const useUserStore = create((set) => ({
profile: null,
settings: {},
preferences: {},
}))
相关技能
- zustand-typescript: TypeScript集成和类型安全模式
- zustand-middleware: 使用persist、devtools和immer中间件
- zustand-advanced-patterns: 订阅、瞬态更新和高级技术