📖 核心概念
- 为什么需要状态管理?
- Zustand vs Redux vs Jotai对比
- Store创建与使用
- 中间件:持久化、Immer、DevTools
- 切片模式:模块化Store
- 异步操作与订阅
💻 代码实现
Zustand Store
✅ typescript
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
import { persist, devtools } from 'zustand/middleware';
import { subscribeWithSelector } from 'zustand/middleware';
// ===== 基础Store =====
interface CartItem {
id: string;
name: string;
price: number;
quantity: number;
}
interface CartStore {
items: CartItem[];
addItem: (item: Omit<CartItem, 'quantity'>) => void;
removeItem: (id: string) => void;
updateQuantity: (id: string, quantity: number) => void;
clearCart: () => void;
totalItems: () => number;
totalPrice: () => number;
}
const useCartStore = create<CartStore>()(
devtools(
persist(
immer((set, get) => ({
items: [],
addItem: (item) => set((state) => {
const existing = state.items.find(i => i.id === item.id);
if (existing) {
existing.quantity += 1;
} else {
state.items.push({ ...item, quantity: 1 });
}
}),
removeItem: (id) => set((state) => {
const index = state.items.findIndex(i => i.id === id);
if (index > -1) state.items.splice(index, 1);
}),
updateQuantity: (id, quantity) => set((state) => {
const item = state.items.find(i => i.id === id);
if (item) item.quantity = Math.max(0, quantity);
}),
clearCart: () => set((state) => { state.items = []; }),
totalItems: () => get().items.reduce((sum, i) => sum + i.quantity, 0),
totalPrice: () => get().items.reduce((sum, i) => sum + i.price * i.quantity, 0),
})),
{ name: 'cart-storage' }
),
{ name: 'CartStore' }
)
);
切片模式与异步
✅ typescript
// ===== 切片模式:模块化Store =====
// stores/authSlice.ts
interface AuthSlice {
user: User | null;
token: string | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
const createAuthSlice: StateCreator<RootState, [], [], AuthSlice> = (set) => ({
user: null,
token: null,
login: async (email, password) => {
const response = await api.login({ email, password });
set({ user: response.user, token: response.token });
localStorage.setItem('token', response.token);
},
logout: () => {
set({ user: null, token: null });
localStorage.removeItem('token');
},
});
// stores/uiSlice.ts
interface UISlice {
theme: 'light' | 'dark';
sidebarOpen: boolean;
toggleTheme: () => void;
toggleSidebar: () => void;
}
const createUISlice: StateCreator<RootState, [], [], UISlice> = (set) => ({
theme: 'dark',
sidebarOpen: false,
toggleTheme: () => set((s) => ({ theme: s.theme === 'dark' ? 'light' : 'dark' })),
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
});
// 合并切片
type RootState = AuthSlice & UISlice;
const useStore = create<RootState>()((...a) => ({
...createAuthSlice(...a),
...createUISlice(...a),
}));
// ===== 组件中使用 =====
function ShoppingCart() {
// 选择性订阅:只有items变化时才重渲染
const items = useCartStore((s) => s.items);
const totalPrice = useCartStore((s) => s.totalPrice());
const addItem = useCartStore((s) => s.addItem);
return (
<div>
{items.map(item => (
<CartItem key={item.id} item={item} />
))}
<div className="total">总计: ¥{totalPrice}</div>
</div>
);
}
// 外部订阅(非React组件中使用)
const unsubscribe = useCartStore.subscribe(
(state) => state.items,
(items) => { console.log('购物车变化:', items); },
{ equalityFn: (a, b) => a.length === b.length }
);
// ===== 异步操作最佳实践 =====
interface AsyncState<T> {
data: T | null;
loading: boolean;
error: string | null;
}
function createAsyncSlice<T>(key: string, fetcher: () => Promise<T>) {
return create<AsyncState<T> & { fetch: () => Promise<void> }>((set) => ({
data: null,
loading: false,
error: null,
fetch: async () => {
set({ loading: true, error: null });
try {
const data = await fetcher();
set({ data, loading: false });
} catch (err) {
set({ error: (err as Error).message, loading: false });
}
},
}));
}
🎯 练习任务
- 1 用Zustand实现一个完整的购物车状态管理
- 2 创建一个带持久化的用户认证Store
- 3 实现切片模式,将大型Store拆分为多个模块
- 4 使用subscribeWithSelector优化组件重渲染
🏆 成就解锁
状态管理专家 — 掌握Zustand状态管理,构建可预测的应用状态