【前端进阶】React状态管理完全指南:从useState到Redux
引言
React作为Facebook开源的前端框架,已经成为现代Web应用开发的主流选择之一。在React应用中,状态管理是核心中的核心,无论是组件内部的状态、跨组件共享的状态,还是服务端同步的状态,都需要精心设计和管理。本文将全面深入地讲解React状态管理的各种方案,从最基本的useState到成熟的Redux架构,帮助读者构建可维护、可扩展的React应用。
一、React状态管理基础
1.1 什么是状态
在React中,状态(State)是指影响组件渲染的数据。与普通JavaScript变量不同,React状态具有响应式特性------当状态发生变化时,组件会自动重新渲染。状态可以分为以下几类:
- UI状态:如弹窗开关、折叠面板、输入框值等
- 业务数据状态:如用户信息、列表数据、缓存数据等
- 服务器状态:如API返回的数据、加载状态、错误信息等
- URL状态:如路由参数、查询字符串等
1.2 状态管理的演进
React状态管理方案经历了从简单到复杂的演进过程:
-
组件本地状态:使用useState/useReducer
-
** Props Drilling**:通过props层层传递
-
Context API:跨层级状态共享
-
第三方状态库:Redux、MobX、Zustand等
┌─────────────────────────────────────────────────────────────┐
│ React State Management │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ useState │ │ useReducer │ │ useContext │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └───────────────────┼───────────────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Context │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌────────────────────┼────────────────────┐ │
│ │ │ │ │
│ ┌──────▼──────┐ ┌───────▼───────┐ ┌──────▼──────┐ │
│ │ Redux │ │ MobX │ │ Zustand │ │
│ └─────────────┘ └───────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
二、useState与useReducer
2.1 useState最佳实践
jsx
// 基础useState使用
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
// 复杂状态 - 对象形式
function UserProfile() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0,
preferences: {
theme: 'light',
language: 'zh-CN',
notifications: true
}
});
// 局部更新user的某个字段
const updateName = (name) => {
setUser(prev => ({ ...prev, name }));
};
// 深层更新嵌套对象
const updatePreference = (key, value) => {
setUser(prev => ({
...prev,
preferences: {
...prev.preferences,
[key]: value
}
}));
};
return (
<div>
<input
value={user.name}
onChange={e => updateName(e.target.value)}
/>
<select
value={user.preferences.theme}
onChange={e => updatePreference('theme', e.target.value)}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
);
}
2.2 useReducer处理复杂状态
jsx
// useReducer - 适用于复杂状态逻辑
import React, { useReducer } from 'react';
// Action Types
const ActionTypes = {
SET_TITLE: 'SET_TITLE',
ADD_TODO: 'ADD_TODO',
TOGGLE_TODO: 'TOGGLE_TODO',
DELETE_TODO: 'DELETE_TODO',
CLEAR_COMPLETED: 'CLEAR_COMPLETED',
SET_FILTER: 'SET_FILTER'
};
// Initial State
const initialState = {
todos: [],
filter: 'all', // 'all', 'active', 'completed'
newTodoTitle: ''
};
// Reducer Function
function todoReducer(state, action) {
switch (action.type) {
case ActionTypes.SET_TITLE:
return { ...state, newTodoTitle: action.payload };
case ActionTypes.ADD_TODO:
if (!state.newTodoTitle.trim()) return state;
const newTodo = {
id: Date.now(),
title: state.newTodoTitle,
completed: false,
createdAt: new Date().toISOString()
};
return {
...state,
todos: [...state.todos, newTodo],
newTodoTitle: ''
};
case ActionTypes.TOGGLE_TODO:
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
};
case ActionTypes.DELETE_TODO:
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
};
case ActionTypes.CLEAR_COMPLETED:
return {
...state,
todos: state.todos.filter(todo => !todo.completed)
};
case ActionTypes.SET_FILTER:
return { ...state, filter: action.payload };
default:
return state;
}
}
// 使用Reducer的组件
function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, initialState);
// 筛选逻辑
const filteredTodos = state.todos.filter(todo => {
if (state.filter === 'active') return !todo.completed;
if (state.filter === 'completed') return todo.completed;
return true;
});
const activeCount = state.todos.filter(t => !t.completed).length;
const completedCount = state.todos.length - activeCount;
return (
<div className="todo-app">
<h1>Todo List</h1>
<form onSubmit={e => {
e.preventDefault();
dispatch({ type: ActionTypes.ADD_TODO });
}}>
<input
type="text"
value={state.newTodoTitle}
onChange={e => dispatch({
type: ActionTypes.SET_TITLE,
payload: e.target.value
})}
placeholder="What needs to be done?"
/>
<button type="submit">Add</button>
</form>
<ul>
{filteredTodos.map(todo => (
<li key={todo.id} className={todo.completed ? 'completed' : ''}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => dispatch({
type: ActionTypes.TOGGLE_TODO,
payload: todo.id
})}
/>
<span>{todo.title}</span>
<button onClick={() => dispatch({
type: ActionTypes.DELETE_TODO,
payload: todo.id
})}>Delete</button>
</li>
))}
</ul>
<div className="footer">
<span>{activeCount} items left</span>
<div className="filters">
{['all', 'active', 'completed'].map(filter => (
<button
key={filter}
className={state.filter === filter ? 'active' : ''}
onClick={() => dispatch({
type: ActionTypes.SET_FILTER,
payload: filter
})}
>
{filter}
</button>
))}
</div>
{completedCount > 0 && (
<button onClick={() => dispatch({
type: ActionTypes.CLEAR_COMPLETED
})}>
Clear completed
</button>
)}
</div>
</div>
);
}
2.3 useState vs useReducer选择
javascript
// 选择建议
/*
* useState 适用场景:
* - 状态类型简单(字符串、数字、布尔值)
* - 状态之间相互独立
* - 组件内状态,不跨组件共享
* - 状态更新逻辑简单
*/
// ✅ useState 合适的例子
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [rememberMe, setRememberMe] = useState(false);
// ...
}
/*
* useReducer 适用场景:
* - 复杂的状态对象
* - 多个状态之间存在关联
* - 状态更新逻辑复杂(涉及多个子值或嵌套更新)
* - 需要实现类似"撤销/重做"的功能
*/
// ✅ useReducer 合适的例子
function ShoppingCart() {
const [cart, dispatch] = useReducer(cartReducer, initialCart);
// cart包含: items[], shippingAddress{}, paymentMethod{},
// couponCode, isCheckingOut, error...
const addItem = (product) => dispatch({ type: 'ADD_ITEM', product });
const removeItem = (itemId) => dispatch({ type: 'REMOVE_ITEM', itemId });
const updateQuantity = (itemId, qty) =>
dispatch({ type: 'UPDATE_QUANTITY', itemId, quantity: qty });
const applyCoupon = (code) =>
dispatch({ type: 'APPLY_COUPON', code });
const checkout = () => dispatch({ type: 'CHECKOUT' });
// ...
}
三、Context API深度使用
3.1 Context基础
jsx
// 创建Context
import React, { createContext, useContext, useState } from 'react';
// 创建Theme Context
const ThemeContext = createContext(undefined);
// Provider组件
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const [primaryColor, setPrimaryColor] = useState('#1890ff');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
setPrimaryColor(prev => prev === '#1890ff' ? '#52c41a' : '#1890ff');
};
const value = {
theme,
primaryColor,
toggleTheme,
isDark: theme === 'dark'
};
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
// 自定义Hook - 方便消费
function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
// 消费Context
function ThemeSwitch() {
const { theme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme}>
Current: {theme}, Click to toggle
</button>
);
}
function ThemedButton() {
const { primaryColor } = useTheme();
return (
<button style={{ backgroundColor: primaryColor }}>
Themed Button
</button>
);
}
3.2 复合组件与Context
jsx
// 使用Context实现表单复合组件
import React, { createContext, useContext, useReducer } from 'react';
// Form Context
const FormContext = createContext(undefined);
// Form State Reducer
function formReducer(state, action) {
switch (action.type) {
case 'UPDATE_FIELD':
return {
...state,
values: {
...state.values,
[action.field]: action.value
},
errors: {
...state.errors,
[action.field]: undefined
}
};
case 'SET_ERROR':
return {
...state,
errors: {
...state.errors,
[action.field]: action.error
}
};
case 'SET_SUBMITTING':
return { ...state, isSubmitting: action.value };
case 'RESET':
return action.initialState;
default:
return state;
}
}
// Form Provider
function Form({ children, initialValues, onSubmit, validate }) {
const [state, dispatch] = useReducer(formReducer, {
values: initialValues,
errors: {},
isSubmitting: false
});
const handleSubmit = async (e) => {
e.preventDefault();
// 验证
if (validate) {
const errors = validate(state.values);
if (Object.keys(errors).length > 0) {
Object.entries(errors).forEach(([field, error]) => {
dispatch({ type: 'SET_ERROR', field, error });
});
return;
}
}
dispatch({ type: 'SET_SUBMITTING', value: true });
try {
await onSubmit(state.values);
} catch (error) {
console.error('Form submission error:', error);
} finally {
dispatch({ type: 'SET_SUBMITTING', value: false });
}
};
const updateField = (field, value) => {
dispatch({ type: 'UPDATE_FIELD', field, value });
};
const value = { state, updateField, dispatch };
return (
<FormContext.Provider value={value}>
<form onSubmit={handleSubmit}>{children}</form>
</FormContext.Provider>
);
}
// 自定义Hook
function useFormField(name) {
const { state, updateField } = useContext(FormContext);
return {
name,
value: state.values[name] || '',
error: state.errors[name],
onChange: (e) => updateField(name, e.target.value)
};
}
// 复合组件
function Input({ label, type = 'text', placeholder, required }) {
const { state } = useContext(FormContext);
// 实现...
}
function FormField({ name, label, required }) {
const { state, updateField } = useContext(FormContext);
const value = state.values[name] || '';
const error = state.errors[name];
return (
<div className={`form-field ${error ? 'has-error' : ''}`}>
<label>{label}</label>
<input
name={name}
value={value}
onChange={e => updateField(name, e.target.value)}
/>
{error && <span className="error">{error}</span>}
</div>
);
}
// 使用示例
function RegistrationForm() {
const handleSubmit = async (values) => {
await fetch('/api/register', {
method: 'POST',
body: JSON.stringify(values)
});
// 处理成功
};
const validate = (values) => {
const errors = {};
if (!values.email) {
errors.email = 'Email is required';
} else if (!/\S+@\S+\.\S+/.test(values.email)) {
errors.email = 'Invalid email format';
}
if (!values.password) {
errors.password = 'Password is required';
} else if (values.password.length < 8) {
errors.password = 'Password must be at least 8 characters';
}
return errors;
};
return (
<Form
initialValues={{ email: '', password: '', username: '' }}
onSubmit={handleSubmit}
validate={validate}
>
<FormField name="email" label="Email" required />
<FormField name="password" label="Password" required />
<FormField name="username" label="Username" />
<button type="submit">Register</button>
</Form>
);
}
3.3 Context性能优化
jsx
// Context性能优化 - 分离读写
import React, { createContext, useContext, useState, useMemo } from 'react';
// 创建两个独立的Context
const UserContext = createContext(undefined);
const UserUpdateContext = createContext(undefined);
// Provider组合
function UserProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const login = async (credentials) => {
setLoading(true);
try {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials)
});
const userData = await response.json();
setUser(userData);
} finally {
setLoading(false);
}
};
const logout = () => {
setUser(null);
};
const updateProfile = async (updates) => {
const response = await fetch('/api/profile', {
method: 'PUT',
body: JSON.stringify(updates)
});
const updatedUser = await response.json();
setUser(updatedUser);
};
// 使用useMemo缓存value,避免不必要的重渲染
const userValue = useMemo(() => ({
user,
loading,
isAuthenticated: !!user
}), [user, loading]);
const updateValue = useMemo(() => ({
login,
logout,
updateProfile
}), []);
return (
<UserContext.Provider value={userValue}>
<UserUpdateContext.Provider value={updateValue}>
{children}
</UserUpdateContext.Provider>
</UserContext.Provider>
);
}
// 分离消费 - 只订阅需要的内容
function useUser() {
return useContext(UserContext);
}
function useUserUpdate() {
return useContext(UserUpdateContext);
}
// 组件中使用
function UserProfile() {
const { user, isAuthenticated } = useUser();
// 只有user变化时重渲染
if (!isAuthenticated) return <LoginPrompt />;
return <div>{user.name}</div>;
}
function LoginButton() {
const { login, logout } = useUserUpdate();
const { isAuthenticated } = useUser();
// 只有login/logout变化时重渲染
return (
<button onClick={isAuthenticated ? logout : login}>
{isAuthenticated ? 'Logout' : 'Login'}
</button>
);
}
四、Redux状态管理
4.1 Redux核心概念
javascript
// Redux核心概念图解
/*
* Redux数据流:
*
* ┌─────────────────────────────────────────────────────┐
* │ UI Layer │
* │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
* │ │Component│ │Component│ │Component│ │
* │ └────┬────┘ └────┬────┘ └────┬────┘ │
* │ │ │ │ │
* │ └──────────────┼──────────────┘ │
* │ │ │
* └──────────────────────┼──────────────────────────────┘
* │ dispatch(action)
* ▼
* ┌──────────────────────────────────────────────────────┐
* │ Action │
* │ { type: 'ADD_TODO', payload: { text: 'Learn Redux' } }│
* └──────────────────────┬──────────────────────────────┘
* │
* ▼
* ┌──────────────────────────────────────────────────────┐
* │ Reducer │
* │ │
* │ (state, action) => newState │
* │ │
* │ ┌─────────────────────────────────────────────┐ │
* │ │ Root Reducer │ │
* │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
* │ │ │ todos │ │filters │ │ users │ │ │
* │ │ └─────────┘ └─────────┘ └─────────┘ │ │
* │ │ │ │ │ │ │
* │ │ └────────────┼────────────┘ │ │
* │ │ ▼ │ │
* │ │ combineReducers() │ │
* │ └─────────────────────────────────────────────┘ │
* └──────────────────────┬──────────────────────────────┘
* │
* ▼
* ┌──────────────────────────────────────────────────────┐
* │ Store │
* │ ┌─────────────────────────────────────────────┐ │
* │ │ getState() │ │
* │ │ dispatch(action) │ │
* │ │ subscribe(listener) │ │
* │ └─────────────────────────────────────────────┘ │
* └──────────────────────────────────────────────────────┘
*/
4.2 Redux Toolkit现代化使用
javascript
// Redux Toolkit - slice示例
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { fetchUserAPI } from './api';
// 异步thunk
export const fetchUser = createAsyncThunk(
'user/fetchUser',
async (userId, { rejectWithValue }) => {
try {
const response = await fetchUserAPI(userId);
return response.data;
} catch (error) {
return rejectWithValue(error.message);
}
}
);
// User Slice
const userSlice = createSlice({
name: 'user',
initialState: {
currentUser: null,
isLoading: false,
error: null
},
reducers: {
// 同步action
updateUser: (state, action) => {
state.currentUser = { ...state.currentUser, ...action.payload };
},
clearUser: (state) => {
state.currentUser = null;
state.error = null;
}
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.isLoading = false;
state.currentUser = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload || 'Failed to fetch user';
});
}
});
export const { updateUser, clearUser } = userSlice.actions;
export default userSlice.reducer;
// Selectors
export const selectCurrentUser = (state) => state.user.currentUser;
export const selectUserLoading = (state) => state.user.isLoading;
export const selectUserError = (state) => state.user.error;
4.3 Store配置与中间件
javascript
// Redux Store配置
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';
import productReducer from './productSlice';
import cartReducer from './cartSlice';
import { loggerMiddleware } from './middleware/logger';
import { apiMiddleware } from './middleware/api';
// 创建store
export const store = configureStore({
reducer: {
user: userReducer,
products: productReducer,
cart: cartReducer
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
// 忽略这些action路径
ignoredActions: ['user/fetchUser/fulfilled'],
// 忽略这些paths
ignoredPaths: ['user.currentUser.metadata.lastLogin']
},
thunk: true
}).concat(loggerMiddleware, apiMiddleware),
devTools: process.env.NODE_ENV !== 'production',
preloadedState: {}
});
// Store类型
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// 自定义中间件示例
const loggerMiddleware = (storeAPI) => (next) => (action) => {
console.group(action.type);
console.log('Current state:', storeAPI.getState());
console.log('Action:', action);
const result = next(action);
console.log('Next state:', storeAPI.getState());
console.groupEnd();
return result;
};
const apiMiddleware = (storeAPI) => (next) => (action) => {
if (action.type === 'api/call') {
const { endpoint, method, body, onSuccess, onError } = action.payload;
fetch(endpoint, { method, body })
.then(response => response.json())
.then(data => storeAPI.dispatch(onSuccess(data)))
.catch(error => storeAPI.dispatch(onError(error)));
}
return next(action);
};
4.4 在React中使用Redux
jsx
// React-Redux连接组件
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {
selectCartItems,
selectCartTotal,
selectCartItemCount,
selectCartLoading
} from './cartSlice';
import {
addToCart,
removeFromCart,
updateQuantity,
clearCart
} from './cartSlice';
// Hooks方式 (推荐)
function Cart() {
const dispatch = useDispatch();
const items = useSelector(selectCartItems);
const total = useSelector(selectCartTotal);
const itemCount = useSelector(selectCartItemCount);
const isLoading = useSelector(selectCartLoading);
const handleAddToCart = (product) => {
dispatch(addToCart(product));
};
const handleRemoveItem = (itemId) => {
dispatch(removeFromCart(itemId));
};
const handleUpdateQuantity = (itemId, quantity) => {
dispatch(updateQuantity({ itemId, quantity }));
};
if (isLoading) return <div>Loading...</div>;
return (
<div className="cart">
<h2>Shopping Cart ({itemCount} items)</h2>
<div className="cart-items">
{items.map(item => (
<div key={item.id} className="cart-item">
<img src={item.image} alt={item.name} />
<div className="item-details">
<h3>{item.name}</h3>
<p>${item.price}</p>
</div>
<div className="quantity-controls">
<button
onClick={() => handleUpdateQuantity(item.id, item.quantity - 1)}
disabled={item.quantity <= 1}
>-</button>
<span>{item.quantity}</span>
<button
onClick={() => handleUpdateQuantity(item.id, item.quantity + 1)}
>+</button>
</div>
<button
className="remove-btn"
onClick={() => handleRemoveItem(item.id)}
>Remove</button>
</div>
))}
</div>
<div className="cart-summary">
<div className="total">
<span>Total:</span>
<span>${total.toFixed(2)}</span>
</div>
<button className="checkout-btn">
Proceed to Checkout
</button>
</div>
</div>
);
}
// 选择器工厂函数 - 带参数
function createStructuredSelector(selectors) {
return (state) => {
const result = {};
for (const key in selectors) {
result[key] = selectors[key](state);
}
return result;
};
}
// 使用示例
function ProductList({ categoryId }) {
const { products, isLoading, error } = useSelector(
createStructuredSelector({
products: (state) => state.products.items.filter(
p => p.categoryId === categoryId
),
isLoading: (state) => state.products.isLoading,
error: (state) => state.products.error
})
);
// ...
}
五、Redux高级模式
5.1 RTK Query服务端状态管理
javascript
// RTK Query - API数据获取
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
const apiSlice = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: '/api',
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token;
if (token) {
headers.set('authorization', `Bearer ${token}`);
}
return headers;
}
}),
tagTypes: ['Product', 'User', 'Order'],
endpoints: (builder) => ({
// 获取产品列表
getProducts: builder.query({
query: ({ page = 1, limit = 20, category }) => ({
url: '/products',
params: { page, limit, category }
}),
providesTags: (result) => {
if (result?.data) {
return [
{ type: 'Product', id: 'LIST' },
...result.data.map(({ id }) => ({ type: 'Product', id }))
];
}
return [{ type: 'Product', id: 'LIST' }];
}
}),
// 获取单个产品
getProduct: builder.query({
query: (id) => `/products/${id}`,
providesTags: (result, error, id) => [{ type: 'Product', id }]
}),
// 创建产品
createProduct: builder.mutation({
query: (product) => ({
url: '/products',
method: 'POST',
body: product
}),
invalidatesTags: [{ type: 'Product', id: 'LIST' }]
}),
// 更新产品
updateProduct: builder.mutation({
query: ({ id, ...product }) => ({
url: `/products/${id}`,
method: 'PATCH',
body: product
}),
invalidatesTags: (result, error, { id }) => [
{ type: 'Product', id },
{ type: 'Product', id: 'LIST' }
]
}),
// 删除产品
deleteProduct: builder.mutation({
query: (id) => ({
url: `/products/${id}`,
method: 'DELETE'
}),
invalidatesTags: (result, error, id) => [{ type: 'Product', id }]
})
})
});
export const {
useGetProductsQuery,
useGetProductQuery,
useCreateProductMutation,
useUpdateProductMutation,
useDeleteProductMutation
} = apiSlice;
jsx
// 在组件中使用RTK Query
import React from 'react';
import { useGetProductsQuery, useUpdateProductMutation } from './apiSlice';
function ProductList() {
const [page, setPage] = React.useState(1);
const {
data,
isLoading,
isFetching,
isError,
error,
refetch
} = useGetProductsQuery({ page, limit: 20 });
const [updateProduct, { isLoading: isUpdating }] = useUpdateProductMutation();
const handleUpdatePrice = async (productId, newPrice) => {
try {
await updateProduct({ id: productId, price: newPrice });
} catch (error) {
console.error('Update failed:', error);
}
};
if (isLoading) return <div>Loading products...</div>;
if (isError) return <div>Error: {error.message}</div>;
return (
<div>
<button onClick={refetch} disabled={isFetching}>
{isFetching ? 'Refreshing...' : 'Refresh'}
</button>
<div className="products">
{data.products.map(product => (
<div key={product.id} className="product-card">
<h3>{product.name}</h3>
<p>Price: ${product.price}</p>
<p>Stock: {product.stock}</p>
<button
onClick={() => handleUpdatePrice(product.id, product.price + 10)}
disabled={isUpdating}
>
Increase Price
</button>
</div>
))}
</div>
<div className="pagination">
<button
onClick={() => setPage(p => Math.max(1, p - 1))}
disabled={page === 1}
>
Previous
</button>
<span>Page {page} of {data.totalPages}</span>
<button
onClick={() => setPage(p => p + 1)}
disabled={page >= data.totalPages}
>
Next
</button>
</div>
</div>
);
}
5.2 Redux订阅与性能
javascript
// 细粒度订阅优化
import React from 'react';
import { useSelector, shallowEqual } from 'react-redux';
// 不好的做法 - 整个state变化就重渲染
function BadComponent() {
const { user, products, orders } = useSelector(state => ({
user: state.user,
products: state.products,
orders: state.orders
}));
// state任何部分变化都会触发重渲染
}
// 好的做法 - 只订阅需要的数据
function GoodComponent() {
// 只订阅需要的字段
const userName = useSelector(state => state.user.name);
const productCount = useSelector(state => state.products.length);
const recentOrders = useSelector(
state => state.orders.slice(0, 5),
shallowEqual // 浅比较数组引用
);
}
// 使用createSelector创建记忆化选择器
import { createSelector } from '@reduxjs/toolkit';
const selectCartItems = state => state.cart.items;
const selectCartItemsMetadata = createSelector(
[selectCartItems],
(items) => ({
totalItems: items.reduce((sum, item) => sum + item.quantity, 0),
uniqueItems: items.length,
totalPrice: items.reduce((sum, item) => sum + item.price * item.quantity, 0)
})
);
function CartSummary() {
const { totalItems, uniqueItems, totalPrice } = useSelector(selectCartItemsMetadata);
return (
<div>
<p>Total Items: {totalItems}</p>
<p>Unique Items: {uniqueItems}</p>
<p>Total Price: ${totalPrice.toFixed(2)}</p>
</div>
);
}
六、状态管理方案选择
6.1 方案对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| useState | 组件本地状态 | 简单、性能好 | 无法跨组件共享 |
| useReducer | 复杂组件状态 | 逻辑集中、易于测试 | 代码量较多 |
| Context | 跨组件传递 | 无需prop drilling | 不适合频繁变化 |
| Redux | 全局状态 | 生态成熟、调试方便 | 学习曲线陡峭 |
| MobX | 响应式状态 | 简洁直观 | 魔法较多 |
| Zustand | 轻量状态 | API简洁、性能好 | 生态较弱 |
| RTK Query | 服务端状态 | 自动缓存、同步 | 仅适于API数据 |
6.2 实际项目建议
javascript
// 推荐的项目状态分层架构
/*
* React项目状态管理架构:
*
* ┌─────────────────────────────────────────────────────────────┐
* │ Server State Layer (RTK Query) │
* │ - API数据获取与缓存 │
* │ - 自动重新获取、轮询 │
* │ - 乐观更新 │
* └─────────────────────────────────────────────────────────────┘
* │
* ▼
* ┌─────────────────────────────────────────────────────────────┐
* │ Global UI State (Zustand/Context) │
* │ - 主题、语言设置 │
* │ - 侧边栏状态 │
* │ - 模态框控制 │
* └─────────────────────────────────────────────────────────────┘
* │
* ▼
* ┌─────────────────────────────────────────────────────────────┐
* │ Domain State (Redux Toolkit) │
* │ - 用户认证状态 │
* │ - 购物车 │
* │ - 复杂业务逻辑 │
* └─────────────────────────────────────────────────────────────┘
* │
* ▼
* ┌─────────────────────────────────────────────────────────────┐
* │ Local Component State (useState) │
* │ - 表单输入 │
* │ - UI交互状态 │
* │ - 临时数据 │
* └─────────────────────────────────────────────────────────────┘
*/
// 具体项目示例
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
// UI状态 - 使用Zustand
const useUIStore = create(
devtools(
persist(
(set) => ({
sidebarOpen: true,
theme: 'light',
language: 'zh-CN',
toggleSidebar: () => set(state => ({ sidebarOpen: !state.sidebarOpen })),
setTheme: (theme) => set({ theme }),
setLanguage: (lang) => set({ language: lang })
}),
{ name: 'ui-storage' }
)
)
);
// 购物车状态 - 使用Zustand(简单场景)
const useCartStore = create((set, get) => ({
items: [],
addItem: (product) => set(state => {
const existing = state.items.find(i => i.id === product.id);
if (existing) {
return {
items: state.items.map(i =>
i.id === product.id
? { ...i, quantity: i.quantity + 1 }
: i
)
};
}
return { items: [...state.items, { ...product, quantity: 1 }] };
}),
removeItem: (id) => set(state => ({
items: state.items.filter(i => i.id !== id)
})),
clearCart: () => set({ items: [] }),
total: () => get().items.reduce((sum, i) => sum + i.price * i.quantity, 0)
}));
// 表单状态 - useState
function ContactForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
// ...
}
七、总结
React状态管理是一个复杂但至关重要的主题。本文从基础出发,系统介绍了useState、useReducer、Context API和Redux四种主要状态管理方案,深入讲解了它们的原理、使用方法和最佳实践。
在实际项目中,没有放之四海而皆准的最优方案,需要根据具体场景和团队情况选择合适的状态管理策略。建议:
- 优先使用简单方案:能用useState解决的就不要引入Redux
- 分层管理状态:按职责将状态分为不同层次
- 遵循不可变原则:确保状态更新的可预测性
- 重视性能优化:使用记忆化选择器避免不必要的重渲染
- 善用工具:Redux DevTools可以极大提升调试效率
希望本文能够帮助读者建立完整的React状态管理知识体系,在实际项目中做出正确的技术决策。