状态管理¶
📚 章节目标¶
本章节将全面介绍前端状态管理的各种方案,包括Redux、MobX、Zustand、Recoil、状态机等,帮助学习者掌握不同状态管理方案的选型和使用。
学习目标¶
- 深入理解状态管理的核心概念
- 掌握Redux及其生态工具
- 掌握MobX响应式状态管理
- 掌握Zustand轻量级状态管理
- 掌握Recoil原子状态管理
- 了解状态机在状态管理中的应用
🎯 状态管理概述¶
1. 什么是状态管理¶
JavaScript
// 状态管理示例
// 不使用状态管理
function App() {
const [user, setUser] = useState(null);
const [todos, setTodos] = useState([]);
const [theme, setTheme] = useState('light');
// 状态分散在各个组件中
return (
<div>
<Header user={user} theme={theme} />
<TodoList todos={todos} setTodos={setTodos} />
<Footer theme={theme} setTheme={setTheme} />
</div>
);
}
// 使用状态管理
function App() {
return (
<Provider store={store}>
<Header />
<TodoList />
<Footer />
</Provider>
);
}
2. 状态管理的必要性¶
2.1 什么时候需要状态管理¶
JavaScript
// 需要状态管理的场景
// 1. 多个组件需要共享状态
// 2. 组件层级很深,传递props困难
// 3. 状态需要持久化
// 4. 需要时间旅行调试
// 5. 需要中间件处理异步逻辑
// 示例:多组件共享状态
// Header组件需要用户信息
function Header() {
const user = useStore(state => state.user);
return <div>Welcome, {user?.name}</div>;
}
// Sidebar组件需要用户信息
function Sidebar() {
const user = useStore(state => state.user);
return <div>{user?.avatar}</div>;
}
// Profile组件需要用户信息
function Profile() {
const user = useStore(state => state.user);
return <div>{user?.bio}</div>;
}
2.2 状态管理方案对比¶
| 方案 | 适用场景 | 学习成本 | 性能 | 生态 |
|---|---|---|---|---|
| Context API | 简单状态共享 | 低 | 中 | 原生 |
| Zustand | 中等复杂度 | 低 | 高 | 良好 |
| Redux Toolkit | 大型应用 | 中 | 高 | 优秀 |
| MobX | 响应式需求 | 中 | 高 | 良好 |
| Recoil | 原子状态 | 中 | 高 | 良好 |
| XState | 复杂状态机 | 高 | 高 | 良好 |
🗃️ Redux深度实践¶
1. Redux基础¶
1.1 核心概念¶
JavaScript
// Action
const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';
const SET_FILTER = 'SET_FILTER';
// Action Creator
function addTodo(text) {
return {
type: ADD_TODO,
payload: { text },
};
}
function toggleTodo(id) {
return {
type: TOGGLE_TODO,
payload: { id },
};
}
function setFilter(filter) {
return {
type: SET_FILTER,
payload: { filter },
};
}
// Reducer
const initialState = {
todos: [],
filter: 'all',
};
function todoReducer(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [
...state.todos,
{
id: Date.now(),
text: action.payload.text,
completed: false,
},
],
};
case TOGGLE_TODO:
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload.id
? { ...todo, completed: !todo.completed }
: todo
),
};
case SET_FILTER:
return {
...state,
filter: action.payload.filter,
};
default:
return state;
}
}
// Store
import { createStore } from 'redux';
const store = createStore(todoReducer);
// Dispatch Action
store.dispatch(addTodo('Learn Redux'));
store.dispatch(toggleTodo(1));
store.dispatch(setFilter('active'));
// Subscribe to Changes
store.subscribe(() => {
console.log(store.getState());
});
1.2 Redux Toolkit¶
JavaScript
// 使用Redux Toolkit简化Redux代码
import { createSlice, configureStore } from '@reduxjs/toolkit';
// Slice
const todoSlice = createSlice({
name: 'todos',
initialState: {
todos: [],
filter: 'all',
},
reducers: {
addTodo: (state, action) => {
state.todos.push({
id: Date.now(),
text: action.payload,
completed: false,
});
},
toggleTodo: (state, action) => {
const todo = state.todos.find(todo => todo.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
},
setFilter: (state, action) => {
state.filter = action.payload;
},
},
});
// Extract actions
export const { addTodo, toggleTodo, setFilter } = todoSlice.actions;
// Create store
const store = configureStore({
reducer: {
todos: todoSlice.reducer,
},
});
// Use in React
import { useSelector, useDispatch } from 'react-redux';
function TodoList() {
const todos = useSelector(state => state.todos.todos);
const filter = useSelector(state => state.todos.filter);
const dispatch = useDispatch();
const filteredTodos = todos.filter(todo => {
if (filter === 'active') return !todo.completed;
if (filter === 'completed') return todo.completed;
return true;
});
return (
<div>
<ul>
{filteredTodos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => dispatch(toggleTodo(todo.id))}
/>
{todo.text}
</li>
))}
</ul>
<button onClick={() => dispatch(addTodo('New Todo'))}>
Add Todo
</button>
</div>
);
}
2. Redux中间件¶
2.1 Thunk中间件¶
JavaScript
import { createSlice, configureStore } from '@reduxjs/toolkit';
import { createAsyncThunk } from '@reduxjs/toolkit';
// Async Thunk
export const fetchTodos = createAsyncThunk(
'todos/fetchTodos',
async () => {
const response = await fetch('/api/todos');
return response.json();
}
);
export const addTodoAsync = createAsyncThunk(
'todos/addTodo',
async (text) => {
const response = await fetch('/api/todos', {
method: 'POST',
body: JSON.stringify({ text }),
});
return response.json();
}
);
// Slice with extraReducers
const todoSlice = createSlice({
name: 'todos',
initialState: {
todos: [],
loading: false,
error: null,
},
reducers: {
toggleTodo: (state, action) => {
const todo = state.todos.find(todo => todo.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
},
},
extraReducers: (builder) => {
builder
.addCase(fetchTodos.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchTodos.fulfilled, (state, action) => {
state.loading = false;
state.todos = action.payload;
})
.addCase(fetchTodos.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
})
.addCase(addTodoAsync.fulfilled, (state, action) => {
state.todos.push(action.payload);
});
},
});
// Create store with thunk middleware
const store = configureStore({
reducer: {
todos: todoSlice.reducer,
},
});
// Use in component
function TodoList() {
const dispatch = useDispatch();
const { todos, loading, error } = useSelector(state => state.todos);
useEffect(() => {
dispatch(fetchTodos());
}, [dispatch]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</div>
);
}
2.2 自定义中间件¶
JavaScript
// Logger中间件
// Redux中间件采用柯里化结构:store => next => action
// 每层函数接收不同参数,形成中间件链
const loggerMiddleware = store => next => action => {
console.log('Dispatching:', action); // 记录发出的action
const result = next(action); // 调用下一个中间件或reducer
console.log('Next State:', store.getState()); // 记录更新后的状态
return result;
};
// 使用中间件
const store = configureStore({
reducer: {
todos: todoSlice.reducer,
},
// getDefaultMiddleware()包含thunk等默认中间件,concat追加自定义中间件
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(loggerMiddleware),
});
// 异步中间件:当action是函数时,直接执行而不传递给reducer
const asyncMiddleware = store => next => action => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState);
}
return next(action);
};
3. Redux最佳实践¶
3.1 模块化¶
JavaScript
// store/modules/todos.js
import { createSlice } from '@reduxjs/toolkit';
const todoSlice = createSlice({
name: 'todos',
initialState: {
items: [],
filter: 'all',
},
reducers: {
// reducers
},
});
export const { addTodo, toggleTodo } = todoSlice.actions;
export default todoSlice.reducer;
// store/modules/users.js
import { createSlice } from '@reduxjs/toolkit';
const userSlice = createSlice({
name: 'users',
initialState: {
currentUser: null,
users: [],
},
reducers: {
// reducers
},
});
export const { login, logout } = userSlice.actions;
export default userSlice.reducer;
// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import todos from './modules/todos';
import users from './modules/users';
const store = configureStore({
reducer: {
todos,
users,
},
});
export default store;
3.2 选择器优化¶
JavaScript
// 基础选择器:从全局state中提取特定片段
const selectTodos = state => state.todos.items;
const selectFilter = state => state.todos.filter;
// 记忆化选择器:只有输入变化时才重新计算,避免不必要的重渲染
import { createSelector } from '@reduxjs/toolkit';
const selectFilteredTodos = createSelector(
[selectTodos, selectFilter], // 输入选择器(依赖)
(todos, filter) => { // 输出函数(仅在依赖变化时执行)
switch (filter) {
case 'active':
return todos.filter(todo => !todo.completed);
case 'completed':
return todos.filter(todo => todo.completed);
default:
return todos;
}
}
);
// 使用选择器
function TodoList() {
const filteredTodos = useSelector(selectFilteredTodos);
return <ul>{filteredTodos.map(todo => <li key={todo.id}>{todo.text}</li>)}</ul>;
}
🎭 MobX响应式状态管理¶
1. MobX基础¶
1.1 核心概念¶
JavaScript
import { makeAutoObservable, observable, action, computed, makeObservable } from 'mobx';
// 方式1: makeAutoObservable
class TodoStore {
todos = [];
filter = 'all';
constructor() {
makeAutoObservable(this);
}
get filteredTodos() {
switch (this.filter) {
case 'active':
return this.todos.filter(todo => !todo.completed);
case 'completed':
return this.todos.filter(todo => todo.completed);
default:
return this.todos;
}
}
addTodo(text) {
this.todos.push({
id: Date.now(),
text,
completed: false,
});
}
toggleTodo(id) {
const todo = this.todos.find(todo => todo.id === id);
if (todo) {
todo.completed = !todo.completed;
}
}
setFilter(filter) {
this.filter = filter;
}
}
// 方式2: 手动装饰器
class TodoStore {
@observable todos = [];
@observable filter = 'all';
@computed get filteredTodos() {
switch (this.filter) {
case 'active':
return this.todos.filter(todo => !todo.completed);
case 'completed':
return this.todos.filter(todo => todo.completed);
default:
return this.todos;
}
}
@action addTodo(text) {
this.todos.push({
id: Date.now(),
text,
completed: false,
});
}
@action toggleTodo(id) {
const todo = this.todos.find(todo => todo.id === id);
if (todo) {
todo.completed = !todo.completed;
}
}
@action setFilter(filter) {
this.filter = filter;
}
}
// 创建store实例
const todoStore = new TodoStore();
1.2 在React中使用MobX¶
JavaScript
import { observer } from 'mobx-react-lite';
// 使用observer包装组件
const TodoList = observer(() => {
const { filteredTodos, addTodo, toggleTodo, setFilter } = todoStore;
return (
<div>
<ul>
{filteredTodos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
{todo.text}
</li>
))}
</ul>
<button onClick={() => addTodo('New Todo')}>Add Todo</button>
<select onChange={(e) => setFilter(e.target.value)}>
<option value="all">All</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
</select>
</div>
);
});
// 使用useLocalObservable
import { useLocalObservable } from 'mobx-react-lite';
function TodoList() {
const store = useLocalObservable(() => ({
todos: [],
filter: 'all',
get filteredTodos() {
switch (this.filter) {
case 'active':
return this.todos.filter(todo => !todo.completed);
case 'completed':
return this.todos.filter(todo => todo.completed);
default:
return this.todos;
}
},
addTodo(text) {
this.todos.push({
id: Date.now(),
text,
completed: false,
});
},
toggleTodo(id) {
const todo = this.todos.find(todo => todo.id === id);
if (todo) {
todo.completed = !todo.completed;
}
},
setFilter(filter) {
this.filter = filter;
},
}));
return (
<div>
<ul>
{store.filteredTodos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => store.toggleTodo(todo.id)}
/>
{todo.text}
</li>
))}
</ul>
<button onClick={() => store.addTodo('New Todo')}>Add Todo</button>
</div>
);
}
2. MobX高级特性¶
2.1 异步操作¶
JavaScript
import { makeAutoObservable, runInAction } from 'mobx';
class UserStore {
users = [];
loading = false;
error = null;
constructor() {
makeAutoObservable(this);
}
async fetchUsers() {
this.loading = true; // 同步修改可以直接赋值
this.error = null;
try {
const response = await fetch('/api/users');
const users = await response.json();
// 异步操作后必须用runInAction包裹状态修改
// 否则MobX无法追踪这些变更,也无法触发响应式更新
runInAction(() => {
this.users = users;
this.loading = false;
});
} catch (error) {
runInAction(() => {
this.error = error.message;
this.loading = false;
});
}
}
async addUser(user) {
this.loading = true;
this.error = null;
try {
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(user),
});
const newUser = await response.json();
// 同理,await之后的状态修改需要runInAction
runInAction(() => {
this.users.push(newUser);
this.loading = false;
});
} catch (error) {
runInAction(() => {
this.error = error.message;
this.loading = false;
});
}
}
}
const userStore = new UserStore();
2.2 reaction和when¶
JavaScript
import { reaction, when } from 'mobx';
// reaction - 监听特定数据变化并执行副作用
// 第一个函数:跟踪的数据(只有返回值变化时才触发)
// 第二个函数:变化后执行的副作用
const dispose = reaction(
() => todoStore.todos.length, // 跟踪表达式
(length) => {
console.log(`Todos count changed to ${length}`);
// 可以在这里保存到localStorage
localStorage.setItem('todos', JSON.stringify(todoStore.todos));
}
);
// when - 等待条件满足后执行一次(自动销毁,适合一次性逻辑)
when(
() => todoStore.todos.length > 0, // 条件表达式
() => {
console.log('First todo added!'); // 条件满足时执行
}
);
// dispose()可手动取消reaction的监听
// dispose();
🎪 Zustand轻量级状态管理¶
1. Zustand基础¶
1.1 基本使用¶
JavaScript
import { create } from 'zustand';
// 创建store
const useStore = create((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 = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
const decrement = useStore((state) => state.decrement);
const reset = useStore((state) => state.reset);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}
// 选择多个状态
function Counter() {
const { count, increment, decrement, reset } = useStore();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}
1.2 复杂状态管理¶
JavaScript
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
// 使用中间件
const useStore = create(
devtools(
persist(
(set, get) => ({
todos: [],
filter: 'all',
addTodo: (text) =>
set((state) => ({
todos: [
...state.todos,
{ id: Date.now(), text, completed: false },
],
})),
toggleTodo: (id) =>
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
),
})),
deleteTodo: (id) =>
set((state) => ({
todos: state.todos.filter((todo) => todo.id !== id),
})),
setFilter: (filter) => set({ filter }),
// ⚠️ 注意:getter 每次访问都会返回新的数组引用,在组件中直接使用
// useStore(state => state.filteredTodos) 会导致无限重渲染。
// 建议:在组件中使用 useShallow 或 useMemo 来避免不必要的重渲染:
// const filteredTodos = useMemo(() => state.filteredTodos, [state.todos, state.filter]);
get filteredTodos() {
const { todos, filter } = get();
switch (filter) {
case 'active':
return todos.filter((todo) => !todo.completed);
case 'completed':
return todos.filter((todo) => todo.completed);
default:
return todos;
}
},
}),
{ name: 'todo-storage' }
)
)
);
// 在组件中使用
function TodoList() {
const todos = useStore((state) => state.todos);
const filter = useStore((state) => state.filter);
const filteredTodos = useStore((state) => state.filteredTodos);
const addTodo = useStore((state) => state.addTodo);
const toggleTodo = useStore((state) => state.toggleTodo);
const deleteTodo = useStore((state) => state.deleteTodo);
const setFilter = useStore((state) => state.setFilter);
return (
<div>
<ul>
{filteredTodos.map((todo) => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
{todo.text}
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
<button onClick={() => addTodo('New Todo')}>Add Todo</button>
<select value={filter} onChange={(e) => setFilter(e.target.value)}>
<option value="all">All</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
</select>
</div>
);
}
2. Zustand高级特性¶
2.1 异步操作¶
JavaScript
import { create } from 'zustand';
const useStore = create((set, get) => ({
users: [],
loading: false,
error: null,
fetchUsers: async () => {
set({ loading: true, error: null });
try { // try/catch捕获异常
const response = await fetch('/api/users');
const users = await response.json();
set({ users, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
addUser: async (user) => {
set({ loading: true, error: null });
try {
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(user),
});
const newUser = await response.json();
set((state) => ({ users: [...state.users, newUser], loading: false }));
} catch (error) {
set({ error: error.message, loading: false });
}
},
}));
// 在组件中使用
function UserList() {
const { users, loading, error, fetchUsers } = useStore();
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
2.2 Slice模式¶
JavaScript
import { create } from 'zustand';
// 创建slice
const createTodoSlice = (set, get) => ({
todos: [],
addTodo: (text) =>
set((state) => ({
todos: [...state.todos, { id: Date.now(), text, completed: false }], // ...展开运算符:展开数组/对象
})),
toggleTodo: (id) =>
set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
),
})),
deleteTodo: (id) =>
set((state) => ({
todos: state.todos.filter((todo) => todo.id !== id),
})),
});
const createFilterSlice = (set) => ({
filter: 'all',
setFilter: (filter) => set({ filter }),
});
// 合并slices
const useStore = create((set, get) => ({
...createTodoSlice(set, get),
...createFilterSlice(set),
get filteredTodos() {
const { todos, filter } = get();
switch (filter) {
case 'active':
return todos.filter((todo) => !todo.completed);
case 'completed':
return todos.filter((todo) => todo.completed);
default:
return todos;
}
},
}));
⚛️ Recoil原子状态管理¶
1. Recoil基础¶
1.1 Atom和Selector¶
JavaScript
import { atom, selector, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
// Atom - 原子状态
const countState = atom({
key: 'countState',
default: 0,
});
const textState = atom({
key: 'textState',
default: '',
});
// Selector - 派生状态
const doubledCountState = selector({
key: 'doubledCountState',
get: ({ get }) => {
const count = get(countState);
return count * 2;
},
});
const filteredTextState = selector({
key: 'filteredTextState',
get: ({ get }) => {
const text = get(textState);
return text.toUpperCase();
},
});
// 在组件中使用
function Counter() {
const [count, setCount] = useRecoilState(countState);
const doubledCount = useRecoilValue(doubledCountState);
return (
<div>
<p>Count: {count}</p>
<p>Doubled: {doubledCount}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
function TextInput() {
const [text, setText] = useRecoilState(textState);
const filteredText = useRecoilValue(filteredTextState);
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<p>Filtered: {filteredText}</p>
</div>
);
}
1.2 异步Selector¶
JavaScript
import { atom, selector, useRecoilValue } from 'recoil';
// 异步selector
const currentUserQuery = selector({
key: 'currentUserQuery',
get: async () => {
const response = await fetch('/api/user');
return response.json();
},
});
// 带参数的异步selector
const userQuery = selectorFamily({
key: 'userQuery',
get: (userId) => async () => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
},
});
// 在组件中使用
function CurrentUser() {
const user = useRecoilValue(currentUserQuery);
return <div>{user?.name}</div>; // ?.可选链:对象为null/undefined时安全返回undefined
}
function UserProfile({ userId }) {
const user = useRecoilValue(userQuery(userId));
return <div>{user?.name}</div>;
}
2. Recoil高级特性¶
2.1 Atom Effects¶
JavaScript
import { atom } from 'recoil';
// Atom Effects:唟桶Atom的副作用钩子,可用于同步localStorage、订阅WebSocket等
const localStorageEffect = (key) => ({ setSelf, onSet }) => {
// 初始化时从 localStorage 恢复值
const savedValue = localStorage.getItem(key);
if (savedValue != null) {
setSelf(JSON.parse(savedValue)); // 设置Atom初始值
}
// 每次Atom值变化时同步到localStorage
onSet((newValue, _, isReset) => {
isReset
? localStorage.removeItem(key) // 重置时清除存储
: localStorage.setItem(key, JSON.stringify(newValue)); // 更新存储
});
};
const counterState = atom({
key: 'counterState',
default: 0,
effects: [localStorageEffect('counter')], // 挂载副作用
});
// 在组件中使用
function Counter() {
const [count, setCount] = useRecoilState(counterState);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
2.2 异步状态管理¶
JavaScript
import { atom, selector, useRecoilState, useRecoilValueLoadable } from 'recoil';
// 异步数据atom
const usersState = atom({
key: 'usersState',
default: selector({
key: 'usersState/default',
get: async () => {
const response = await fetch('/api/users');
return response.json();
},
}),
});
// 在组件中使用
function UserList() {
const usersLoadable = useRecoilValueLoadable(usersState);
switch (usersLoadable.state) {
case 'hasValue':
return (
<ul>
{usersLoadable.contents.map((user) => ( // map转换每个元素;filter筛选;reduce累积
<li key={user.id}>{user.name}</li>
))}
</ul>
);
case 'loading':
return <div>Loading...</div>;
case 'hasError':
return <div>Error: {usersLoadable.contents.message}</div>;
}
}
🤖 XState状态机¶
1. XState基础¶
1.1 简单状态机¶
JavaScript
import { createMachine, assign } from 'xstate';
import { useMachine } from '@xstate/react';
// 创建状态机
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
on: { TOGGLE: 'active' },
},
active: {
on: { TOGGLE: 'inactive' },
},
},
});
// 在组件中使用
function ToggleButton() {
const [state, send] = useMachine(toggleMachine);
return (
<button onClick={() => send('TOGGLE')}>
{state.matches('active') ? 'ON' : 'OFF'}
</button>
);
}
1.2 带上下文的状态机¶
JavaScript
import { createMachine, assign } from 'xstate';
import { useMachine } from '@xstate/react';
// 创建带上下文的状态机
const counterMachine = createMachine({
id: 'counter',
initial: 'idle',
context: {
count: 0,
},
states: {
idle: {
on: {
INCREMENT: {
target: 'idle',
actions: assign({
count: (context) => context.count + 1,
}),
},
DECREMENT: {
target: 'idle',
actions: assign({
count: (context) => context.count - 1,
}),
},
RESET: {
target: 'idle',
actions: assign({
count: () => 0,
}),
},
},
},
},
});
// 在组件中使用
function Counter() {
const [state, send] = useMachine(counterMachine);
return (
<div>
<p>Count: {state.context.count}</p>
<button onClick={() => send('INCREMENT')}>+</button>
<button onClick={() => send('DECREMENT')}>-</button>
<button onClick={() => send('RESET')}>Reset</button>
</div>
);
}
2. XState高级特性¶
2.1 异步状态机¶
JavaScript
import { createMachine, assign } from 'xstate';
import { useMachine } from '@xstate/react';
// 创建异步状态机:用invoke处理异步操作
const fetchMachine = createMachine({
id: 'fetch',
initial: 'idle',
context: {
data: null,
error: null,
},
states: {
idle: {
on: { FETCH: 'loading' }, // 收到FETCH事件时转入loading状态
},
loading: {
// invoke:进入该状态时自动执行异步任务
invoke: {
src: async () => { // async定义异步函数;await等待Promise完成
const response = await fetch('/api/data'); // await等待异步操作完成
return response.json();
},
onDone: { // 异步成功:转入success状态并存储数据
target: 'success',
actions: assign({
data: (_, event) => event.data,
}),
},
onError: { // 异步失败:转入failure状态并记录错误
target: 'failure',
actions: assign({
error: (_, event) => event.data,
}),
},
},
},
success: {
on: { FETCH: 'loading' }, // 可以重新加载
},
failure: {
on: { FETCH: 'loading' }, // 可以重试
},
},
});
// 在组件中使用
function DataFetcher() {
const [state, send] = useMachine(fetchMachine); // 解构赋值:从对象/数组提取值
return (
<div>
<button onClick={() => send('FETCH')}>Fetch Data</button>
{state.matches('loading') && <div>Loading...</div>}
{state.matches('success') && <div>Data: {JSON.stringify(state.context.data)}</div>}
{state.matches('failure') && <div>Error: {state.context.error}</div>}
</div>
);
}
2.2 并行状态¶
JavaScript
import { createMachine, assign } from 'xstate';
// 创建并行状态机:多个状态区域可以同时独立运行
const parallelMachine = createMachine({ // const不可重新赋值;let块级作用域变量
id: 'parallel',
initial: 'active',
states: {
active: {
type: 'parallel', // 并行状态:子状态同时活跃、独立转换
states: {
// 并行区域1:计数器
counter: {
initial: 'idle',
states: {
idle: {
on: {
INCREMENT: {
target: 'idle',
actions: assign({
count: (context) => (context.count || 0) + 1,
}),
},
},
},
},
},
// 并行区域2:开关(与计数器完全独立)
toggle: {
initial: 'off',
states: {
off: {
on: { TOGGLE: 'on' },
},
on: {
on: { TOGGLE: 'off' },
},
},
},
},
},
},
});
📊 状态管理方案对比¶
1. 选型决策树¶
Text Only
项目需求
├── 状态复杂度
│ ├── 简单状态 → Context API / Zustand
│ ├── 中等复杂度 → Zustand / Recoil
│ ├── 大型应用 → Redux Toolkit / MobX
│ └── 复杂业务流程 → XState
├── 团队经验
│ ├── Redux经验 → Redux Toolkit
│ ├── 响应式经验 → MobX
│ ├── 新手友好 → Zustand
│ └── 实验性 → Recoil
└── 性能要求
├── 高性能 → Zustand / MobX
├── 可预测性 → Redux / XState
└── 灵活性 → MobX / Recoil
2. 方案对比表¶
| 特性 | Redux | MobX | Zustand | Recoil | XState |
|---|---|---|---|---|---|
| 学习曲线 | 陡峭 | 中等 | 平缓 | 中等 | 陡峭 |
| 代码量 | 多 | 少 | 少 | 中等 | 中等 |
| 性能 | 高 | 高 | 高 | 高 | 高 |
| 调试工具 | 优秀 | 良好 | 良好 | 良好 | 优秀 |
| 时间旅行 | 支持 | 支持 | 支持 | 支持 | 支持 |
| 异步处理 | Thunk/Saga | 内置 | 内置 | 内置 | 内置 |
| 社区生态 | 优秀 | 良好 | 良好 | 良好 | 良好 |
📝 练习题¶
1. 基础题¶
题目1:使用Zustand实现一个计数器
JavaScript
import { create } from 'zustand';
// 创建计数器store
const useCounterStore = create((set) => ({ // 箭头函数:简洁的函数语法
// 实现计数器逻辑
}));
// 在组件中使用
function Counter() {
// 使用store
return (
<div>
<button>+</button>
<span>0</span>
<button>-</button>
</div>
);
}
2. 进阶题¶
题目2:使用MobX实现一个待办事项列表
JavaScript
import { makeAutoObservable } from 'mobx';
// 创建TodoStore
class TodoStore {
// 实现待办事项逻辑
}
// 在组件中使用
function TodoList() {
// 使用TodoStore
return (
<div>
{/* 渲染待办事项列表 */}
</div>
);
}
3. 面试题¶
题目3:比较Redux和MobX的区别
JavaScript
// 答案要点:
// 1. Redux是单向数据流,MobX是响应式数据
// 2. Redux使用纯函数reducer,MobX使用可变状态
// 3. Redux需要大量样板代码,MobX代码更简洁
// 4. Redux更易于调试,MobX更易于理解
// 5. Redux适合大型应用,MobX适合中小型应用
🎯 本章总结¶
本章节全面介绍了前端状态管理的各种方案,包括Redux、MobX、Zustand、Recoil、XState等。关键要点:
- 状态管理概念:理解状态管理的必要性和适用场景
- Redux:掌握Redux Toolkit和最佳实践
- MobX:掌握响应式状态管理
- Zustand:掌握轻量级状态管理
- Recoil:掌握原子状态管理
- XState:掌握状态机在状态管理中的应用
- 方案选型:根据项目需求选择合适的状态管理方案
下一步将深入学习前端性能优化技术。