外部状态管理 #
一、概述 #
1.1 为什么需要外部状态管理 #
| 场景 | 说明 |
|---|---|
| 复杂应用 | 大量共享状态 |
| 跨组件通信 | 深层嵌套组件 |
| 状态持久化 | 本地存储同步 |
| 调试需求 | 时间旅行调试 |
1.2 常用状态管理库 #
| 库 | 特点 |
|---|---|
| Redux | 可预测、中间件丰富 |
| Zustand | 轻量、简单 |
| Jotai | 原子化状态 |
| MobX | 响应式 |
二、Redux 集成 #
2.1 安装 #
bash
npm install @reduxjs/toolkit react-redux
2.2 配置 compat #
javascript
// vite.config.js
import { defineConfig } from 'vite';
import preact from '@preact/preset-vite';
export default defineConfig({
plugins: [preact()],
resolve: {
alias: {
'react': 'preact/compat',
'react-dom': 'preact/compat'
}
}
});
2.3 创建 Store #
jsx
// store.js
import { configureStore, createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
}
}
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
});
2.4 使用 Redux #
jsx
import { Provider, useSelector, useDispatch } from 'react-redux';
import { store, increment, decrement } from './store';
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
function Counter() {
const count = useSelector(state => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}
2.5 异步 Action #
jsx
import { createAsyncThunk } from '@reduxjs/toolkit';
export const fetchUser = createAsyncThunk(
'user/fetchUser',
async (userId) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
);
const userSlice = createSlice({
name: 'user',
initialState: { data: null, loading: false, error: null },
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
}
});
// 使用
function UserProfile({ userId }) {
const { data, loading, error } = useSelector(state => state.user);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchUser(userId));
}, [userId]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return <div>{data.name}</div>;
}
三、Zustand 集成 #
3.1 安装 #
bash
npm install zustand
3.2 创建 Store #
jsx
import { create } from 'zustand';
const useStore = create((set, get) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
incrementByAmount: (amount) => set((state) => ({
count: state.count + amount
})),
reset: () => set({ count: 0 }),
getCount: () => get().count
}));
3.3 使用 Store #
jsx
function Counter() {
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
const decrement = useStore((state) => state.decrement);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
3.4 选择器优化 #
jsx
// 选择特定状态
const count = useStore((state) => state.count);
// 选择多个状态
const { count, increment } = useStore((state) => ({
count: state.count,
increment: state.increment
}));
// 使用 shallow 比较
import { shallow } from 'zustand/shallow';
const { count, increment } = useStore(
(state) => ({ count: state.count, increment: state.increment }),
shallow
);
3.5 异步操作 #
jsx
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 });
}
}
}));
// 使用
function UserProfile({ userId }) {
const { user, loading, error, fetchUser } = useUserStore();
useEffect(() => {
fetchUser(userId);
}, [userId]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return <div>{user.name}</div>;
}
3.6 持久化 #
jsx
import { persist } from 'zustand/middleware';
const useCartStore = create(
persist(
(set) => ({
items: [],
addItem: (item) => set((state) => ({
items: [...state.items, item]
})),
removeItem: (id) => set((state) => ({
items: state.items.filter(item => item.id !== id)
})),
clearCart: () => set({ items: [] })
}),
{
name: 'cart-storage',
getStorage: () => localStorage
}
)
);
四、Jotai 集成 #
4.1 安装 #
bash
npm install jotai
4.2 基本用法 #
jsx
import { atom, useAtom } from 'jotai';
const countAtom = atom(0);
function Counter() {
const [count, setCount] = useAtom(countAtom);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
);
}
4.3 派生 Atom #
jsx
const firstNameAtom = atom('John');
const lastNameAtom = atom('Doe');
const fullNameAtom = atom((get) => {
return `${get(firstNameAtom)} ${get(lastNameAtom)}`;
});
function NameDisplay() {
const [fullName] = useAtom(fullNameAtom);
return <p>Full Name: {fullName}</p>;
}
4.4 异步 Atom #
jsx
const userIdAtom = atom(1);
const userAtom = atom(async (get) => {
const userId = get(userIdAtom);
const response = await fetch(`/api/users/${userId}`);
return response.json();
});
function UserProfile() {
const [user] = useAtom(userAtom);
return <div>{user?.name}</div>;
}
五、选择指南 #
5.1 对比 #
| 库 | 体积 | 学习曲线 | 适用场景 |
|---|---|---|---|
| Redux | 较大 | 陡峭 | 大型企业应用 |
| Zustand | 很小 | 平缓 | 中小型应用 |
| Jotai | 很小 | 平缓 | 原子化状态 |
| Signals | 很小 | 平缓 | Preact 原生 |
5.2 选择建议 #
jsx
// 小型项目:Signals 或 Zustand
// 中型项目:Zustand 或 Jotai
// 大型项目:Redux Toolkit
// Preact 项目:优先使用 Signals
六、最佳实践 #
6.1 状态分离 #
jsx
// 分离不同领域的状态
const useUserStore = create(/* ... */);
const useCartStore = create(/* ... */);
const useUIStore = create(/* ... */);
6.2 类型安全 #
typescript
import { create } from 'zustand';
interface UserState {
user: User | null;
login: (credentials: Credentials) => Promise<void>;
logout: () => void;
}
const useUserStore = create<UserState>((set) => ({
user: null,
login: async (credentials) => { /* ... */ },
logout: () => set({ user: null })
}));
6.3 DevTools #
jsx
// Redux DevTools
import { devtools } from 'zustand/middleware';
const useStore = create(
devtools((set) => ({
// ...
}))
);
七、总结 #
| 库 | 特点 | 推荐场景 |
|---|---|---|
| Redux | 可预测、中间件 | 大型应用 |
| Zustand | 轻量、简单 | 中小型应用 |
| Jotai | 原子化 | 精细控制 |
| Signals | Preact 原生 | Preact 项目 |
核心原则:
- 根据项目规模选择
- 保持状态结构清晰
- 合理拆分 Store
- 使用 TypeScript 增强类型安全
最后更新:2026-03-28