# React 状态管理
# 状态管理概述
在 React 应用中,状态管理是构建用户界面的核心概念之一。随着应用规模的增长,管理状态变得越来越复杂,尤其是当需要跨多个组件共享状态时。React 生态系统提供了多种状态管理解决方案,从内置的简单 API 到功能强大的第三方库。
# 何时需要状态管理解决方案?
- 多个组件需要访问和修改相同的状态
- 组件树的不同分支需要共享状态
- 需要将 UI 状态与业务逻辑分离
- 状态变更需要触发一系列副作用
- 应用程序状态需要持久化或同步
- 需要预测和调试状态变化
# React 内置状态管理
# useState Hook
React 内置的最基本状态管理工具,适用于组件级别的状态:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
# useReducer Hook
处理复杂状态逻辑的内置解决方案,使用类似 Redux 的 reducer 模式:
import { useReducer } from 'react';
// 定义 reducer 函数
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
throw new Error('Unknown action');
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}
# Context API
React 提供的跨组件共享状态的内置解决方案:
import { createContext, useContext, useState } from 'react';
// 创建上下文
const ThemeContext = createContext();
// 提供者组件
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// 消费者组件
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{
background: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#333' : '#fff'
}}
>
Toggle Theme
</button>
);
}
// 应用
function App() {
return (
<ThemeProvider>
<div>
<h1>Theme Example</h1>
<ThemedButton />
</div>
</ThemeProvider>
);
}
Context API 的优点:
- 原生支持,不需要额外依赖
- 简单直观,易于理解
- 适合中小型应用和静态主题等场景
缺点:
- 没有内置的优化机制,可能导致不必要的渲染
- 组合多个 Context 可能导致"嵌套地狱"
- 不方便进行时间旅行调试和状态持久化
# Context + useReducer
结合 Context 和 useReducer 可以创建简单但强大的状态管理解决方案:
import { createContext, useContext, useReducer } from 'react';
// 创建上下文
const StoreContext = createContext();
// 定义初始状态和 reducer
const initialState = { count: 0, user: null };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'setUser':
return { ...state, user: action.payload };
default:
return state;
}
}
// 创建 Provider
function StoreProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<StoreContext.Provider value={{ state, dispatch }}>
{children}
</StoreContext.Provider>
);
}
// 使用 Hook
function useStore() {
const context = useContext(StoreContext);
if (!context) {
throw new Error('useStore must be used within a StoreProvider');
}
return context;
}
// 使用
function UserInfo() {
const { state, dispatch } = useStore();
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'setUser', payload: { name: 'John' } })}>
Set User
</button>
</div>
);
}
function App() {
return (
<StoreProvider>
<UserInfo />
</StoreProvider>
);
}
这种模式的优点:
- 不需要额外的库
- 拥有类似 Redux 的可预测性
- 较好的代码组织和可维护性
# Redux
Redux 是最流行的 React 状态管理库之一,以其可预测性和强大的开发者工具著称。
# 核心概念
- 单一数据源:整个应用的状态存储在单个 store 的对象树中
- 状态是只读的:改变状态的唯一方法是触发 action
- 使用纯函数进行修改:通过 reducer 函数指定状态如何变化
# 基本示例
import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';
// 定义 action 类型
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
// 创建 action creators
function increment() {
return { type: INCREMENT };
}
function decrement() {
return { type: DECREMENT };
}
// 创建 reducer
function counterReducer(state = { count: 0 }, action) {
switch (action.type) {
case INCREMENT:
return { count: state.count + 1 };
case DECREMENT:
return { count: state.count - 1 };
default:
return state;
}
}
// 创建 store
const store = createStore(counterReducer);
// 组件
function Counter() {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
# Redux Toolkit (RTK)
Redux Toolkit 是官方推荐的编写 Redux 逻辑的方式,简化了配置和常见用例:
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { Provider, useSelector, useDispatch } from 'react-redux';
// 创建 slice
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: state => {
state.count += 1; // Immer 允许"直接修改"状态
},
decrement: state => {
state.count -= 1;
},
incrementByAmount: (state, action) => {
state.count += action.payload;
}
}
});
// 导出 action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// 创建 store
const store = configureStore({
reducer: counterSlice.reducer
});
// 组件
function Counter() {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
</div>
);
}
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
# RTK Query
Redux Toolkit 的数据获取和缓存扩展:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
// 定义 API
const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: 'https://api.example.com/' }),
endpoints: (builder) => ({
getUser: builder.query({
query: (id) => `users/${id}`
}),
updateUser: builder.mutation({
query: ({ id, ...patch }) => ({
url: `users/${id}`,
method: 'PATCH',
body: patch
})
})
})
});
// 导出 hooks
export const { useGetUserQuery, useUpdateUserMutation } = api;
// 创建 store
const store = configureStore({
reducer: {
[api.reducerPath]: api.reducer
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(api.middleware)
});
// 组件
function UserProfile({ userId }) {
const { data: user, isLoading, error } = useGetUserQuery(userId);
const [updateUser, { isLoading: isUpdating }] = useUpdateUserMutation();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{user.name}</h1>
<button
onClick={() => updateUser({ id: userId, name: 'Updated Name' })}
disabled={isUpdating}
>
Update Name
</button>
</div>
);
}
function App() {
return (
<Provider store={store}>
<UserProfile userId="1" />
</Provider>
);
}
# Zustand
Zustand 是一个轻量级的状态管理库,使用简单且性能优异。它使用 hook 暴露状态和操作,没有 Redux 的大量样板代码。
# 基本用法
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, increment, decrement, reset } = useStore();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}
# 选择性订阅状态
Zustand 允许组件只订阅它们需要的状态部分,有助于避免不必要的重新渲染:
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);
# 中间件和持久化
import create from 'zustand';
import { persist } from 'zustand/middleware';
const useStore = create(
persist(
(set) => ({
user: null,
login: (userData) => set({ user: userData }),
logout: () => set({ user: null })
}),
{
name: 'user-storage', // 唯一名称
getStorage: () => localStorage // 使用 localStorage
}
)
);
function Profile() {
const { user, login, logout } = useStore();
return (
<div>
{user ? (
<>
<h1>Welcome, {user.name}</h1>
<button onClick={logout}>Logout</button>
</>
) : (
<button onClick={() => login({ name: 'John' })}>Login</button>
)}
</div>
);
}
# Zustand 的优势
- 极简 API,易于学习和使用
- 没有 Provider 包装器,减少嵌套
- 内置支持 Immer、中间件和 TypeScript
- 体积小(约 1KB)
- 可预测的状态管理
- 良好的性能(避免不必要的渲染)
# Jotai
Jotai 是一个原子化的状态管理库,受 Recoil 启发,专注于原子级别的状态共享。
# 原子概念
Jotai 中最基本的单位是"原子"(atom),它代表一个可以独立更新和订阅的小型状态单元:
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(count + 1)}>+</button>
</div>
);
}
# 派生原子
Jotai 允许从其他原子派生新原子,类似于计算属性:
import { atom, useAtom } from 'jotai';
const countAtom = atom(0);
const doubleCountAtom = atom(
(get) => get(countAtom) * 2
);
function DoubleCounter() {
const [count, setCount] = useAtom(countAtom);
const [doubleCount] = useAtom(doubleCountAtom);
return (
<div>
<p>Count: {count}</p>
<p>Double Count: {doubleCount}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
# 可写派生原子
派生原子还可以是可写的:
import { atom, useAtom } from 'jotai';
const countAtom = atom(0);
const countryAtom = atom('Japan');
// 可读可写派生原子
const doubledCountAtom = atom(
(get) => get(countAtom) * 2,
(get, set, newValue) => set(countAtom, newValue / 2)
);
// 组合多个原子的派生原子
const formattedAtom = atom(
(get) => `Count: ${get(countAtom)}, Country: ${get(countryAtom)}`,
(get, set, { count, country }) => {
if (count !== undefined) set(countAtom, count);
if (country !== undefined) set(countryAtom, country);
}
);
# Jotai 的优势
- 原子化状态管理,精细控制重新渲染
- 适用于表单和高度交互的组件
- 几乎没有样板代码
- 支持异步操作
- 可调试、可持久化、可测试
- 体积小巧(约 2KB)
# Recoil
Recoil 是 Facebook 开发的状态管理库,使用类似 Jotai 的原子模型,但提供了更多专为大型应用设计的功能。
# 基本概念
Recoil 使用两个主要概念:Atoms(原子)和 Selectors(选择器):
import { RecoilRoot, atom, selector, useRecoilState, useRecoilValue } from 'recoil';
// 定义原子
const countAtom = atom({
key: 'countAtom', // 唯一标识符
default: 0 // 默认值
});
// 定义选择器(派生状态)
const doubleCountSelector = selector({
key: 'doubleCountSelector',
get: ({ get }) => {
const count = get(countAtom);
return count * 2;
}
});
function Counter() {
const [count, setCount] = useRecoilState(countAtom);
const doubleCount = useRecoilValue(doubleCountSelector);
return (
<div>
<p>Count: {count}</p>
<p>Double Count: {doubleCount}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
function App() {
return (
<RecoilRoot>
<Counter />
</RecoilRoot>
);
}
# 异步选择器
Recoil 支持异步数据获取:
import { atom, selector, useRecoilState, useRecoilValue } from 'recoil';
const userIdAtom = atom({
key: 'userId',
default: 1
});
const userSelector = selector({
key: 'user',
get: async ({ get }) => {
const id = get(userIdAtom);
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
return response.json();
}
});
function UserInfo() {
const [userId, setUserId] = useRecoilState(userIdAtom);
const user = useRecoilValue(userSelector);
return (
<div>
<p>User ID: {userId}</p>
<button onClick={() => setUserId(userId + 1)}>Next User</button>
<h2>User Details</h2>
<p>Name: {user.name}</p>
<p>Email: {user.email}</p>
</div>
);
}
# Recoil 的优势
- 全面支持异步状态和数据流
- 通过唯一键支持代码分割
- 提供原子快照和时间旅行调试
- 适合大型应用的状态管理
- React Concurrent Mode 友好
# MobX
MobX 是一个通过函数响应式编程使状态管理变得简单和可扩展的库。它允许通过可观察对象直接修改状态。
# 基本概念
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';
// 创建 Store
class CounterStore {
count = 0;
constructor() {
makeAutoObservable(this);
}
increment() {
this.count += 1;
}
decrement() {
this.count -= 1;
}
get doubleCount() {
return this.count * 2;
}
}
const counterStore = new CounterStore();
// 组件
const Counter = observer(() => {
return (
<div>
<p>Count: {counterStore.count}</p>
<p>Double Count: {counterStore.doubleCount}</p>
<button onClick={() => counterStore.increment()}>+</button>
<button onClick={() => counterStore.decrement()}>-</button>
</div>
);
});
# MobX 的优势
- 简单直接的更新模式
- 更少的样板代码
- 高效的响应式系统
- 基于类的架构(面向对象)
- 强大的派生计算属性
- 自动跟踪,只有需要更新的组件才会更新
# 状态管理库对比与选择指南
库 | 体积 | 复杂度 | 性能 | 适用场景 | 特点 |
---|---|---|---|---|---|
Context API | 0 (内置) | 低 | 中 | 小型应用、主题、认证 | 内置、无需第三方依赖 |
Redux | ~4.5KB | 高 | 高 | 大型应用、复杂状态逻辑 | 可预测性、开发工具强大 |
Redux Toolkit | ~13KB | 中 | 高 | 中大型应用 | 简化Redux、内置最佳实践 |
Zustand | ~1KB | 低 | 高 | 各种规模、快速开发 | 简单、灵活、性能好 |
Jotai | ~2KB | 低 | 高 | 细粒度状态、表单 | 原子化、无样板代码 |
Recoil | ~20KB | 中 | 高 | 大型应用、异步状态 | 异步支持好、扩展性强 |
MobX | ~4KB | 中 | 高 | 中大型应用、OOP风格 | 简单直观、响应式 |
# 如何选择状态管理方案?
应用规模和复杂度
- 小型应用:useState, useReducer, Context API
- 中型应用:Zustand, Jotai, Redux Toolkit
- 大型应用:Redux, Recoil, MobX
团队熟悉度
- Redux 生态系统广泛,社区支持强
- Zustand/Jotai 对 React 开发者友好
- MobX 适合喜欢 OOP 的开发者
性能要求
- 高性能需求:Zustand, Jotai, MobX
- 更精细的组件更新:MobX, Jotai
- 高度优化大应用:Redux + memoization
开发体验
- 最少样板代码:Zustand, Jotai
- 最佳调试体验:Redux DevTools
- 类型安全:Redux Toolkit, Zustand
特殊需求
- 异步数据流:RTK Query, Recoil
- 服务器状态:React Query + 任何状态管理
- 持久化:都支持,但实现方式不同
# 最佳实践
# 分层状态管理
一个有效的策略是根据状态的作用范围选择不同的方案:
- 本地状态:使用 useState/useReducer
- 共享非服务器状态:使用 Zustand/Jotai/Context
- 全局 UI 状态:使用 Zustand/Jotai/Context
- 服务器状态和缓存:使用 React Query/SWR/RTK Query
- 复杂领域状态:使用 Redux/MobX
# 状态设计原则
无论选择哪个库,都应遵循这些原则:
- 最小化状态:只存储必要的数据,派生其他内容
- 规范化复杂数据:避免嵌套和重复
- 按领域划分状态:将相关状态分组
- 不可变更新:无论使用哪种库,理解不可变更新的重要性
- 避免过度全局化:并非所有状态都需要全局管理
# 性能优化
- 选择性订阅:只订阅组件需要的状态部分
- 记忆化:使用 useMemo/React.memo/记忆化选择器
- 懒加载状态:按需加载状态模块
- 批量更新:一次性更新多个相关状态
- 分析渲染次数:使用 React DevTools 识别不必要的渲染
# 总结
React 状态管理是一个丰富而不断发展的领域,每种解决方案都有其优势和适用场景。了解不同方案的特点,并基于项目需求、团队经验和性能要求做出明智的选择至关重要。
随着 React 的发展和状态管理库的创新,保持对最新趋势的了解并愿意适应新方法将使你能够构建更高效、可维护的 React 应用。
← React Hooks 组件库设计 →