Frameworks
状态管理
使用 Zustand 进行应用状态管理
状态管理
Vibetake 集成了 Zustand,提供简单而强大的状态管理解决方案。
特性
- 🎯 简单直观的 API
- 📦 轻量级(2kb gzipped)
- 🔄 支持异步操作
- 🛠️ TypeScript 友好
- 🧪 易于测试
基本使用
创建 Store
// src/stores/user-store.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface User {
id: string;
name: string;
email: string;
}
interface UserState {
user: User | null;
isLoading: boolean;
setUser: (user: User) => void;
clearUser: () => void;
updateUser: (updates: Partial<User>) => void;
}
export const useUserStore = create<UserState>()(
persist(
(set, get) => ({
user: null,
isLoading: false,
setUser: (user) => set({ user }),
clearUser: () => set({ user: null }),
updateUser: (updates) => {
const currentUser = get().user;
if (currentUser) {
set({ user: { ...currentUser, ...updates } });
}
},
}),
{
name: 'user-storage', // 本地存储键名
partialize: (state) => ({ user: state.user }), // 只持久化 user 字段
}
)
);
在组件中使用
'use client';
import { useUserStore } from '@/stores/user-store';
export default function UserProfile() {
const { user, setUser, clearUser, updateUser } = useUserStore();
const handleLogin = () => {
setUser({
id: '1',
name: '张三',
email: 'zhangsan@example.com'
});
};
const handleUpdateName = () => {
updateUser({ name: '李四' });
};
if (!user) {
return (
<button onClick={handleLogin} className="btn-primary">
登录
</button>
);
}
return (
<div className="space-y-4">
<h2>用户信息</h2>
<p>姓名: {user.name}</p>
<p>邮箱: {user.email}</p>
<div className="space-x-2">
<button onClick={handleUpdateName} className="btn-secondary">
更新姓名
</button>
<button onClick={clearUser} className="btn-danger">
登出
</button>
</div>
</div>
);
}
高级用法
异步操作
// src/stores/todo-store.ts
import { create } from 'zustand';
interface Todo {
id: string;
title: string;
completed: boolean;
}
interface TodoState {
todos: Todo[];
isLoading: boolean;
error: string | null;
fetchTodos: () => Promise<void>;
addTodo: (title: string) => Promise<void>;
toggleTodo: (id: string) => void;
deleteTodo: (id: string) => Promise<void>;
}
export const useTodoStore = create<TodoState>((set, get) => ({
todos: [],
isLoading: false,
error: null,
fetchTodos: async () => {
set({ isLoading: true, error: null });
try {
const response = await fetch('/api/todos');
const todos = await response.json();
set({ todos, isLoading: false });
} catch (error) {
set({ error: '获取待办事项失败', isLoading: false });
}
},
addTodo: async (title) => {
set({ isLoading: true });
try {
const response = await fetch('/api/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title }),
});
const newTodo = await response.json();
set(state => ({
todos: [...state.todos, newTodo],
isLoading: false
}));
} catch (error) {
set({ error: '添加待办事项失败', isLoading: false });
}
},
toggleTodo: (id) => {
set(state => ({
todos: state.todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
}));
},
deleteTodo: async (id) => {
try {
await fetch(`/api/todos/${id}`, { method: 'DELETE' });
set(state => ({
todos: state.todos.filter(todo => todo.id !== id)
}));
} catch (error) {
set({ error: '删除待办事项失败' });
}
},
}));
选择器优化
import { useUserStore } from '@/stores/user-store';
import { shallow } from 'zustand/shallow';
// 只订阅特定字段,避免不必要的重渲染
export default function UserName() {
const userName = useUserStore(state => state.user?.name);
return <span>{userName}</span>;
}
// 订阅多个字段
export default function UserActions() {
const { setUser, clearUser } = useUserStore(
state => ({ setUser: state.setUser, clearUser: state.clearUser }),
shallow
);
// ...
}
中间件使用
import { create } from 'zustand';
import { devtools, subscribeWithSelector } from 'zustand/middleware';
export const useAppStore = create<AppState>()(
devtools(
subscribeWithSelector(
persist(
(set, get) => ({
// store 实现
}),
{ name: 'app-storage' }
)
),
{ name: 'app-store' }
)
);
// 订阅状态变化
useAppStore.subscribe(
state => state.user,
(user) => {
console.log('用户状态变化:', user);
}
);
最佳实践
- 模块化 - 将不同功能的状态分离到不同的 store
- 选择器优化 - 使用选择器避免不必要的重渲染
- 类型安全 - 充分利用 TypeScript 类型检查
- 持久化 - 合理使用 persist 中间件
- 测试 - 为 store 编写单元测试