𝑻𝒆𝒏𝑪𝒍𝒂𝒘正在头脑风暴···
𝑻𝒆𝒏𝑲𝒊𝑺𝒆𝒀𝒂の𝑨𝒈𝒆𝒏𝒕助手
𝑻𝒆𝒏-𝒇𝒍𝒂𝒔𝒉

React状态管理完全指南

还记得我刚接触React的时候,状态管理这个概念让我一度很困惑。什么时候该用useState?什么时候该用useReducer?什么时候又该引入Redux?今天我就以过来人的身份,系统地为大家梳理一下React中各种状态管理方案的使用场景和最佳实践。

一、状态管理概述

什么是状态管理?

在React中,状态(State)是指数据在应用运行过程中可能发生变化的部分。状态管理则是组织和维护这些数据的方式。

简单来说:

// 简单状态
const [count, setCount] = useState(0)

// 复杂状态
const [user, setUser] = useState({ name: '张三', age: 25 })

// 组件间共享的状态
const [theme, setTheme] = useState('light')

为什么需要状态管理?

随着应用复杂度增加,状态管理会面临以下挑战:

  1. 状态提升:多个组件需要共享状态时,需要将状态提升到共同的祖先组件
  2. 状态同步:不同层级组件之间的状态同步变得复杂
  3. 状态持久化:需要将状态保存到本地存储或服务器
  4. 状态历史:需要撤销/重做功能
  5. 状态调试:难以追踪状态变化的原因

状态管理的演进

  1. 本地状态:组件内部的状态(useState、useReducer)
  2. 状态提升:将状态提升到共同的父组件
  3. Context API:跨组件共享状态
  4. Redux:集中式状态管理
  5. 其他方案:Zustand、Jotai、Recoil等

二、内置状态管理

useState - 基础状态

import { useState } from 'react'

function Counter() {
// 基础用法
const [count, setCount] = useState(0)

return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
)
}

// 对象状态
function UserProfile() {
const [user, setUser] = useState({
name: '张三',
age: 25,
email: 'zhangsan@example.com'
})

const updateName = (newName) => {
setUser(prevUser => ({
...prevUser,
name: newName
}))
}

return (
<div>
<p>Name: {user.name}</p>
<button onClick={() => updateName('李四')}>Change Name</button>
</div>
)
}

// 数组状态
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Learn React', completed: false },
{ id: 2, text: 'Build app', completed: false }
])

const addTodo = (text) => {
setTodos(prevTodos => [
...prevTodos,
{ id: Date.now(), text, completed: false }
])
}

const toggleTodo = (id) => {
setTodos(prevTodos => prevTodos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
))
}

return (
<div>
{todos.map(todo => (
<div key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
</div>
))}
</div>
)
}

useReducer - 复杂状态

当状态逻辑变得复杂时,useReducer提供了更好的组织方式。

import { useReducer } from 'react'

// 定义状态类型
const initialState = {
name: '张三',
age: 25,
email: 'zhangsan@example.com',
loading: false,
error: null
}

// 定义action类型
const ACTIONS = {
SET_NAME: 'SET_NAME',
SET_AGE: 'SET_AGE',
SET_EMAIL: 'SET_EMAIL',
SET_LOADING: 'SET_LOADING',
SET_ERROR: 'SET_ERROR',
RESET: 'RESET'
}

// reducer函数
function userReducer(state, action) {
switch (action.type) {
case ACTIONS.SET_NAME:
return { ...state, name: action.payload }
case ACTIONS.SET_AGE:
return { ...state, age: action.payload }
case ACTIONS.SET_EMAIL:
return { ...state, email: action.payload }
case ACTIONS.SET_LOADING:
return { ...state, loading: action.payload }
case ACTIONS.SET_ERROR:
return { ...state, error: action.payload }
case ACTIONS.RESET:
return initialState
default:
return state
}
}

function UserProfile() {
const [state, dispatch] = useReducer(userReducer, initialState)

const handleNameChange = (e) => {
dispatch({ type: ACTIONS.SET_NAME, payload: e.target.value })
}

const handleAgeChange = (e) => {
const age = parseInt(e.target.value)
dispatch({ type: ACTIONS.SET_AGE, payload: age })
}

const handleEmailChange = (e) => {
dispatch({ type: ACTIONS.SET_EMAIL, payload: e.target.value })
}

return (
<div>
<h2>User Profile</h2>

<div>
<label>Name:</label>
<input
type="text"
value={state.name}
onChange={handleNameChange}
/>
</div>

<div>
<label>Age:</label>
<input
type="number"
value={state.age}
onChange={handleAgeChange}
/>
</div>

<div>
<label>Email:</label>
<input
type="email"
value={state.email}
onChange={handleEmailChange}
/>
</div>

<p>Name: {state.name}</p>
<p>Age: {state.age}</p>
<p>Email: {state.email}</p>
</div>
)
}

// 更复杂的reducer示例
const todoReducer = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, {
id: Date.now(),
text: action.payload,
completed: false
}]
}
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
}
case 'DELETE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
}
case 'SET_FILTER':
return {
...state,
filter: action.payload
}
default:
return state
}
}

function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, {
todos: [],
filter: 'ALL'
})

return (
<div>
<input
type="text"
placeholder="Add todo"
onKeyPress={(e) => {
if (e.key === 'Enter') {
dispatch({ type: 'ADD_TODO', payload: e.target.value })
e.target.value = ''
}
}}
/>

<div>
<button onClick={() => dispatch({ type: 'SET_FILTER', payload: 'ALL' })}>
All
</button>
<button onClick={() => dispatch({ type: 'SET_FILTER', payload: 'ACTIVE' })}>
Active
</button>
<button onClick={() => dispatch({ type: 'SET_FILTER', payload: 'COMPLETED' })}>
Completed
</button>
</div>

<ul>
{state.todos.filter(todo => {
if (state.filter === 'ACTIVE') return !todo.completed
if (state.filter === 'COMPLETED') return todo.completed
return true
}).map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => dispatch({ type: 'TOGGLE_TODO', payload: todo.id })}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => dispatch({ type: 'DELETE_TODO', payload: todo.id })}>
Delete
</button>
</li>
))}
</ul>
</div>
)
}

useMemo 和 useCallback - 性能优化

import { useState, useMemo, useCallback } from 'react'

function ExpensiveComponent({ data }) {
// 使用useMemo缓存计算结果
const processedData = useMemo(() => {
console.log('Processing data...')
return data.map(item => ({
...item,
squared: item.value * item.value,
cubed: item.value * item.value * item.value
}))
}, [data])

return (
<div>
{processedData.map((item, index) => (
<div key={index}>
<p>Value: {item.value}</p>
<p>Squared: {item.squared}</p>
<p>Cubed: {item.cubed}</p>
</div>
))}
</div>
)
}

function ParentComponent() {
const [count, setCount] = useState(0)
const [items] = useState([
{ id: 1, value: 1 },
{ id: 2, value: 2 },
{ id: 3, value: 3 }
])

// 使用useCallback缓存函数
const handleClick = useCallback(() => {
console.log('Button clicked')
}, [])

return (
<div>
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
<ExpensiveComponent data={items} />
<button onClick={handleClick}>Click me</button>
</div>
)
}

三、Context API - 组件间状态共享

基础用法

import { createContext, useContext, useState, useReducer } from 'react'

// 创建Context
const UserContext = createContext()
const ThemeContext = createContext()

// 创建Provider组件
function AppProviders({ children }) {
const [user, setUser] = useState({ name: '张三', age: 25 })
const [theme, setTheme] = useState('light')

const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light')
}

return (
<UserContext.Provider value={{ user, setUser }}>
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
</UserContext.Provider>
)
}

// 自定义Hook
function useUser() {
const context = useContext(UserContext)
if (!context) {
throw new Error('useUser must be used within an AppProviders')
}
return context
}

function useTheme() {
const context = useContext(ThemeContext)
if (!context) {
throw new Error('useTheme must be used within an AppProviders')
}
return context
}

// 使用Context的组件
function UserProfile() {
const { user, setUser } = useUser()

const handleNameChange = (e) => {
setUser(prevUser => ({ ...prevUser, name: e.target.value }))
}

return (
<div>
<h2>User Profile</h2>
<p>Name: {user.name}</p>
<input
type="text"
value={user.name}
onChange={handleNameChange}
/>
</div>
)
}

function ThemeToggle() {
const { theme, toggleTheme } = useTheme()

return (
<div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
<button onClick={toggleTheme}>
Toggle Theme ({theme})
</button>
</div>
)
}

function App() {
return (
<AppProviders>
<div>
<h1>React Context Example</h1>
<UserProfile />
<ThemeToggle />
</div>
</AppProviders>
)
}

复杂Context + useReducer

// 购物车Context
const CartContext = createContext()

// 购物车reducer
const cartReducer = (state, action) => {
switch (action.type) {
case 'ADD_ITEM':
const existingItem = state.items.find(item => item.id === action.payload.id)

if (existingItem) {
return {
...state,
items: state.items.map(item =>
item.id === action.payload.id
? { ...item, quantity: item.quantity + 1 }
: item
)
}
} else {
return {
...state,
items: [...state.items, { ...action.payload, quantity: 1 }]
}
}

case 'REMOVE_ITEM':
return {
...state,
items: state.items.filter(item => item.id !== action.payload)
}

case 'UPDATE_QUANTITY':
return {
...state,
items: state.items.map(item =>
item.id === action.payload.id
? { ...item, quantity: action.payload.quantity }
: item
)
}

case 'CLEAR_CART':
return {
...state,
items: []
}

case 'SET_LOADING':
return {
...state,
loading: action.payload
}

default:
return state
}
}

// 购物车Provider
function CartProvider({ children }) {
const [state, dispatch] = useReducer(cartReducer, {
items: [],
loading: false
})

const addItem = (item) => {
dispatch({ type: 'ADD_ITEM', payload: item })
}

const removeItem = (id) => {
dispatch({ type: 'REMOVE_ITEM', payload: id })
}

const updateQuantity = (id, quantity) => {
dispatch({ type: 'UPDATE_QUANTITY', payload: { id, quantity } })
}

const clearCart = () => {
dispatch({ type: 'CLEAR_CART' })
}

const setLoading = (loading) => {
dispatch({ type: 'SET_LOADING', payload: loading })
}

return (
<CartContext.Provider value={{
...state,
addItem,
removeItem,
updateQuantity,
clearCart,
setLoading
}}>
{children}
</CartContext.Provider>
)
}

// 自定义Hook
function useCart() {
const context = useContext(CartContext)
if (!context) {
throw new Error('useCart must be used within a CartProvider')
}
return context
}

// 购物车组件
function CartItem({ item }) {
const { removeItem, updateQuantity } = useCart()

return (
<li>
<span>{item.name} - ¥{item.price} x {item.quantity}</span>
<button onClick={() => removeItem(item.id)}>Remove</button>
<input
type="number"
value={item.quantity}
onChange={(e) => updateQuantity(item.id, parseInt(e.target.value) || 1)}
min="1"
/>
</li>
)
}

function ShoppingCart() {
const { items, clearCart } = useCart()

const totalPrice = items.reduce((total, item) => total + item.price * item.quantity, 0)

return (
<div>
<h2>Shopping Cart</h2>
<ul>
{items.map(item => (
<CartItem key={item.id} item={item} />
))}
</ul>
<div>
<p>Total: ¥{totalPrice}</p>
<button onClick={clearCart}>Clear Cart</button>
</div>
</div>
)
}

// 产品组件
function ProductList() {
const [products] = useState([
{ id: 1, name: 'iPhone 13', price: 5999 },
{ id: 2, name: 'MacBook Pro', price: 12999 },
{ id: 3, name: 'AirPods Pro', price: 1999 }
])

const { addItem } = useCart()

return (
<div>
<h2>Products</h2>
<ul>
{products.map(product => (
<li key={product.id}>
<span>{product.name} - ¥{product.price}</span>
<button onClick={() => addItem(product)}>Add to Cart</button>
</li>
))}
</ul>
</div>
)
}

function ECommerceApp() {
return (
<CartProvider>
<div>
<h1>E-commerce Store</h1>
<ProductList />
<ShoppingCart />
</div>
</CartProvider>
)
}

四、Redux - 集中式状态管理

基础Redux概念

Redux遵循三个基本原则:

  1. 单一数据源:整个应用的状态存储在一个单一的store中
  2. 状态是只读的:不能直接修改状态,必须通过action来修改
  3. 纯函数执行:通过reducer函数来处理state和action
# 安装Redux
npm install redux react-redux

简单Redux示例

// store.js
import { createStore } from 'redux'

// 初始状态
const initialState = {
count: 0,
user: null,
todos: []
}

// Action Types
const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'
const SET_USER = 'SET_USER'
const ADD_TODO = 'ADD_TODO'

// Action Creators
export const increment = () => ({ type: INCREMENT })
export const decrement = () => ({ type: DECREMENT })
export const setUser = (user) => ({ type: SET_USER, payload: user })
export const addTodo = (todo) => ({ type: ADD_TODO, payload: todo })

// Reducer
function counterReducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 }
case DECREMENT:
return { ...state, count: state.count - 1 }
case SET_USER:
return { ...state, user: action.payload }
case ADD_TODO:
return {
...state,
todos: [...state.todos, { ...action.payload, id: Date.now() }]
}
default:
return state
}
}

// 创建store
export const store = createStore(counterReducer)
// Counter.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { increment, decrement } from './store'

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>
)
}

// UserProfile.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { setUser } from './store'

function UserProfile() {
const user = useSelector(state => state.user)
const dispatch = useDispatch()

const handleSetUser = () => {
dispatch(setUser({ name: '张三', age: 25 }))
}

return (
<div>
<h2>User Profile</h2>
{user ? (
<p>Name: {user.name}, Age: {user.age}</p>
) : (
<p>No user</p>
)}
<button onClick={handleSetUser}>Set User</button>
</div>
)
}

// App.js
import React from 'react'
import { Provider } from 'react-redux'
import store from './store'
import Counter from './Counter'
import UserProfile from './UserProfile'

function App() {
return (
<Provider store={store}>
<div>
<h1>Redux Example</h1>
<Counter />
<UserProfile />
</div>
</Provider>
)
}

Redux Toolkit - 简化Redux

# 安装Redux Toolkit
npm install @reduxjs/toolkit react-redux
// store.js
import { configureStore, createSlice } from '@reduxjs/toolkit'

// 用户Slice
const userSlice = createSlice({
name: 'user',
initialState: { name: '', age: 0 },
reducers: {
setUser: (state, action) => {
state.name = action.payload.name
state.age = action.payload.age
},
clearUser: (state) => {
state.name = ''
state.age = 0
}
}
})

// 计数器Slice
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: (state) => {
state.count += 1
},
decrement: (state) => {
state.count -= 1
},
incrementByAmount: (state, action) => {
state.count += action.payload
}
}
})

// To-Do Slice
const todoSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
addTodo: (state, action) => {
state.push({
id: Date.now(),
text: action.payload,
completed: false
})
},
toggleTodo: (state, action) => {
const todo = state.find(todo => todo.id === action.payload)
if (todo) {
todo.completed = !todo.completed
}
},
removeTodo: (state, action) => {
return state.filter(todo => todo.id !== action.payload)
}
}
})

// 导出action creators
export const { setUser, clearUser } = userSlice.actions
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export const { addTodo, toggleTodo, removeTodo } = todoSlice.actions

// 导出reducer
export const userReducer = userSlice.reducer
export const counterReducer = counterSlice.reducer
export const todoReducer = todoSlice.reducer

// 配置store
export const store = configureStore({
reducer: {
user: userReducer,
counter: counterReducer,
todos: todoReducer
}
})
// App.js
import React from 'react'
import { Provider } from 'react-redux'
import { store } from './store'
import Counter from './Counter'
import UserProfile from './UserProfile'
import TodoList from './TodoList'

function App() {
return (
<Provider store={store}>
<div>
<h1>Redux Toolkit Example</h1>
<Counter />
<UserProfile />
<TodoList />
</div>
</Provider>
)
}

// Counter.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { increment, decrement, incrementByAmount } from './store'

function Counter() {
const count = useSelector(state => state.counter.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>
)
}

// UserProfile.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { setUser, clearUser } from './store'

function UserProfile() {
const user = useSelector(state => state.user)
const dispatch = useDispatch()

const handleSetUser = () => {
dispatch(setUser({ name: '张三', age: 25 }))
}

const handleClearUser = () => {
dispatch(clearUser())
}

return (
<div>
<h2>User Profile</h2>
{user.name ? (
<div>
<p>Name: {user.name}</p>
<p>Age: {user.age}</p>
</div>
) : (
<p>No user</p>
)}
<button onClick={handleSetUser}>Set User</button>
<button onClick={handleClearUser}>Clear User</button>
</div>
)
}

// TodoList.js
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { addTodo, toggleTodo, removeTodo } from './store'

function TodoList() {
const [inputValue, setInputValue] = React.useState('')
const todos = useSelector(state => state.todos)
const dispatch = useDispatch()

const handleAddTodo = () => {
if (inputValue.trim()) {
dispatch(addTodo(inputValue))
setInputValue('')
}
}

return (
<div>
<h2>Todo List</h2>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={handleAddTodo}>Add Todo</button>

<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => dispatch(toggleTodo(todo.id))}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => dispatch(removeTodo(todo.id))}>Delete</button>
</li>
))}
</ul>
</div>
)
}

五、第三方状态管理库

Zustand - 简单的状态管理

# 安装Zustand
npm install zustand
// store.js
import { create } from 'zustand'

// 基础Store
const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 })
}))

// 复杂Store
const useTodoStore = create((set) => ({
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
)
})),
removeTodo: (id) => set((state) => ({
todos: state.todos.filter(todo => todo.id !== id)
})),
clearTodos: () => set({ todos: [] })
}))

// 用户Store
const useUserStore = create((set) => ({
user: null,
setUser: (user) => set({ user }),
clearUser: () => set({ user: null })
}))

// 导出Store
export { useCounterStore, useTodoStore, useUserStore }
// Counter.js
import React from 'react'
import { useCounterStore } from './store'

function Counter() {
const { count, increment, decrement, reset } = useCounterStore()

return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
)
}

// TodoList.js
import React from 'react'
import { useTodoStore } from './store'

function TodoList() {
const [inputValue, setInputValue] = React.useState('')
const { todos, addTodo, toggleTodo, removeTodo, clearTodos } = useTodoStore()

const handleAddTodo = () => {
if (inputValue.trim()) {
addTodo(inputValue)
setInputValue('')
}
}

return (
<div>
<h2>Todo List</h2>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={handleAddTodo}>Add Todo</button>
<button onClick={clearTodos}>Clear All</button>

<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
)
}

// UserProfile.js
import React from 'react'
import { useUserStore } from './store'

function UserProfile() {
const { user, setUser, clearUser } = useUserStore()

const handleSetUser = () => {
setUser({ name: '张三', age: 25 })
}

return (
<div>
<h2>User Profile</h2>
{user ? (
<div>
<p>Name: {user.name}</p>
<p>Age: {user.age}</p>
</div>
) : (
<p>No user</p>
)}
<button onClick={handleSetUser}>Set User</button>
<button onClick={clearUser}>Clear User</button>
</div>
)
}

Jotai - 原子化状态管理

# 安装Jotai
npm install jotai
// store.js
import { atom, useAtom } from 'jotai'

// 基础原子
const countAtom = atom(0)
const doubleCountAtom = atom(
(get) => get(countAtom) * 2
)

// 复杂原子
const userAtom = atom(null)
const todosAtom = atom([])

// 导出原子
export { countAtom, doubleCountAtom, userAtom, todosAtom }
// Counter.js
import React from 'react'
import { countAtom, doubleCountAtom } from './store'
import { useAtom } from 'jotai'

function Counter() {
const [count, setCount] = useAtom(countAtom)
const [doubleCount] = useAtom(doubleCountAtom)

return (
<div>
<p>Count: {count}</p>
<p>Double Count: {doubleCount}</p>
<button onClick={() => setCount(c => c + 1)}>Increment</button>
<button onClick={() => setCount(c => c - 1)}>Decrement</button>
</div>
)
}

// TodoList.js
import React from 'react'
import { todosAtom } from './store'
import { useAtom } from 'jotai'

function TodoList() {
const [todos, setTodos] = useAtom(todosAtom)
const [inputValue, setInputValue] = React.useState('')

const addTodo = () => {
if (inputValue.trim()) {
setTodos(prev => [...prev, { id: Date.now(), text: inputValue, completed: false }])
setInputValue('')
}
}

const toggleTodo = (id) => {
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
))
}

const removeTodo = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id))
}

return (
<div>
<h2>Todo List</h2>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={addTodo}>Add Todo</button>

<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
)
}

六、状态管理方案对比

方案适用场景优点缺点学习曲线
useState简单组件状态简单直接,内置复杂状态管理困难
useReducer复杂组件状态逻辑清晰,可预测代码较长
Context API跨组件共享状态内置,无需额外依赖性能问题,状态更新频繁时可能影响性能
Redux大型应用,复杂状态强大的开发者工具,可预测的状态管理学习成本高,样板代码多
Redux ToolkitRedux的现代化版本简化Redux语法,提高开发效率仍有一定学习成本
Zustand中小型应用简单易学,轻量级不适合超大型应用
Jotai原子化状态管理精细控制状态,优化性能概念较新,社区资源较少

选择建议:

  1. 小型项目/组件useState + useReducer
  2. 中型项目/跨组件共享Context API + useReducer
  3. 大型项目/复杂状态:Redux Toolkit
  4. 快速开发/小型应用:Zustand
  5. 需要精细控制状态:Jotai

七、实战案例

案例1:电商应用状态管理

// 使用Redux Toolkit实现电商应用
import { createSlice, configureStore } from '@reduxjs/toolkit'
import { Provider, useSelector, useDispatch } from 'react-redux'

// 用户Slice
const userSlice = createSlice({
name: 'user',
initialState: {
currentUser: null,
isLoading: false,
error: null
},
reducers: {
loginStart: (state) => {
state.isLoading = true
state.error = null
},
loginSuccess: (state, action) => {
state.currentUser = action.payload
state.isLoading = false
state.error = null
},
loginFailure: (state, action) => {
state.isLoading = false
state.error = action.payload
},
logout: (state) => {
state.currentUser = null
}
}
})

// 产品Slice
const productSlice = createSlice({
name: 'products',
initialState: {
products: [],
isLoading: false,
error: null
},
reducers: {
fetchProductsStart: (state) => {
state.isLoading = true
},
fetchProductsSuccess: (state, action) => {
state.products = action.payload
state.isLoading = false
state.error = null
},
fetchProductsFailure: (state, action) => {
state.isLoading = false
state.error = action.payload
}
}
})

// 购物车Slice
const cartSlice = createSlice({
name: 'cart',
initialState: {
items: [],
total: 0
},
reducers: {
addToCart: (state, action) => {
const item = state.items.find(item => item.id === action.payload.id)

if (item) {
item.quantity += 1
} else {
state.items.push({ ...action.payload, quantity: 1 })
}

state.total = state.items.reduce((total, item) => total + item.price * item.quantity, 0)
},
removeFromCart: (state, action) => {
state.items = state.items.filter(item => item.id !== action.payload)
state.total = state.items.reduce((total, item) => total + item.price * item.quantity, 0)
},
updateQuantity: (state, action) => {
const { id, quantity } = action.payload
const item = state.items.find(item => item.id === id)

if (item) {
item.quantity = quantity
state.total = state.items.reduce((total, item) => total + item.price * item.quantity, 0)
}
},
clearCart: (state) => {
state.items = []
state.total = 0
}
}
})

// 订单Slice
const orderSlice = createSlice({
name: 'orders',
initialState: {
orders: [],
currentOrder: null,
isLoading: false
},
reducers: {
createOrderStart: (state) => {
state.isLoading = true
},
createOrderSuccess: (state, action) => {
state.currentOrder = action.payload
state.orders.push(action.payload)
state.isLoading = false
},
createOrderFailure: (state, action) => {
state.isLoading = false
state.error = action.payload
}
}
})

// 导出action creators
export const {
loginStart,
loginSuccess,
loginFailure,
logout
} = userSlice.actions

export const {
fetchProductsStart,
fetchProductsSuccess,
fetchProductsFailure
} = productSlice.actions

export const {
addToCart,
removeFromCart,
updateQuantity,
clearCart
} = cartSlice.actions

export const {
createOrderStart,
createOrderSuccess,
createOrderFailure
} = orderSlice.actions

// 配置store
export const store = configureStore({
reducer: {
user: userSlice.reducer,
products: productSlice.reducer,
cart: cartSlice.reducer,
orders: orderSlice.reducer
}
})

// 组件
function ProductList() {
const { products, isLoading, error } = useSelector(state => state.products)
const dispatch = useDispatch()

React.useEffect(() => {
dispatch(fetchProductsStart())
// 模拟API调用
setTimeout(() => {
const mockProducts = [
{ id: 1, name: 'iPhone 13', price: 5999, image: 'iphone.jpg' },
{ id: 2, name: 'MacBook Pro', price: 12999, image: 'macbook.jpg' },
{ id: 3, name: 'AirPods Pro', price: 1999, image: 'airpods.jpg' }
]
dispatch(fetchProductsSuccess(mockProducts))
}, 1000)
}, [dispatch])

if (isLoading) return <div>Loading products...</div>
if (error) return <div>Error: {error}</div>

return (
<div>
<h2>Products</h2>
<div style={{ display: 'flex', gap: '20px' }}>
{products.map(product => (
<div key={product.id} style={{ border: '1px solid #ccc', padding: '10px' }}>
<h3>{product.name}</h3>
<p>¥{product.price}</p>
<button onClick={() => dispatch(addToCart(product))}>
Add to Cart
</button>
</div>
))}
</div>
</div>
)
}

function ShoppingCart() {
const { items, total } = useSelector(state => state.cart)
const dispatch = useDispatch()

return (
<div>
<h2>Shopping Cart</h2>
{items.length === 0 ? (
<p>Your cart is empty</p>
) : (
<>
<ul>
{items.map(item => (
<li key={item.id} style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
<span>{item.name} - ¥{item.price}</span>
<input
type="number"
value={item.quantity}
onChange={(e) => dispatch(updateQuantity({ id: item.id, quantity: parseInt(e.target.value) || 1 }))}
min="1"
style={{ width: '50px' }}
/>
<button onClick={() => dispatch(removeFromCart(item.id))}>Remove</button>
</li>
))}
</ul>
<div>
<p>Total: ¥{total}</p>
<button onClick={() => dispatch(clearCart())}>Clear Cart</button>
</div>
</>
)}
</div>
)
}

function LoginForm() {
const [email, setEmail] = React.useState('')
const [password, setPassword] = React.useState('')
const { isLoading, error } = useSelector(state => state.user)
const dispatch = useDispatch()

const handleSubmit = (e) => {
e.preventDefault()
dispatch(loginStart())

// 模拟登录
setTimeout(() => {
dispatch(loginSuccess({ id: 1, name: '张三', email }))
}, 1000)
}

return (
<div>
<h2>Login</h2>
{isLoading && <p>Loading...</p>}
{error && <p style={{ color: 'red' }}>{error}</p>}

<form onSubmit={handleSubmit}>
<div>
<label>Email:</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div>
<label>Password:</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
<button type="submit" disabled={isLoading}>Login</button>
</form>
</div>
)
}

function ECommerceApp() {
const { currentUser } = useSelector(state => state.user)
const dispatch = useDispatch()

const handleLogout = () => {
dispatch(logout())
}

return (
<Provider store={store}>
<div>
<h1>E-commerce Store</h1>

{currentUser ? (
<div>
<div style={{ float: 'right' }}>
<span>Welcome, {currentUser.name}</span>
<button onClick={handleLogout}>Logout</button>
</div>
<div style={{ clear: 'both' }}>
<ProductList />
<ShoppingCart />
</div>
</div>
) : (
<LoginForm />
)}
</div>
</Provider>
)
}

案例2:待办事项应用 - 多方案对比

// 使用不同方案实现同一个功能
import React from 'react'

// 方案1:useState
function TodoAppWithUseState() {
const [todos, setTodos] = React.useState([])
const [inputValue, setInputValue] = React.useState('')

const addTodo = () => {
if (inputValue.trim()) {
setTodos(prev => [...prev, {
id: Date.now(),
text: inputValue,
completed: false
}])
setInputValue('')
}
}

const toggleTodo = (id) => {
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
))
}

const removeTodo = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id))
}

return (
<div>
<h2>Todos (useState)</h2>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={addTodo}>Add</button>

<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
)
}

// 方案2:useReducer
function TodoAppWithUseReducer() {
const [state, dispatch] = React.useReducer(todoReducer, { todos: [] })

return (
<div>
<h2>Todos (useReducer)</h2>
<TodoInput dispatch={dispatch} />
<TodoList todos={state.todos} dispatch={dispatch} />
</div>
)
}

function TodoInput({ dispatch }) {
const [inputValue, setInputValue] = React.useState('')

const addTodo = () => {
if (inputValue.trim()) {
dispatch({ type: 'ADD_TODO', payload: inputValue })
setInputValue('')
}
}

return (
<div>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={addTodo}>Add</button>
</div>
)
}

function TodoList({ todos, dispatch }) {
return (
<ul>
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} dispatch={dispatch} />
))}
</ul>
)
}

function TodoItem({ todo, dispatch }) {
return (
<li>
<input
type="checkbox"
checked={todo.completed}
onChange={() => dispatch({ type: 'TOGGLE_TODO', payload: todo.id })}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => dispatch({ type: 'DELETE_TODO', payload: todo.id })}>
Delete
</button>
</li>
)
}

function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, {
id: Date.now(),
text: action.payload,
completed: false
}]
}
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
}
case 'DELETE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
}
default:
return state
}
}

// 方案3:Context API
function TodoAppWithContext() {
return (
<TodoProvider>
<div>
<h2>Todos (Context API)</h2>
<TodoInputContext />
<TodoListContext />
</div>
</TodoProvider>
)
}

const TodoContext = React.createContext()

function TodoProvider({ children }) {
const [todos, setTodos] = React.useState([])

const addTodo = (text) => {
if (text.trim()) {
setTodos(prev => [...prev, {
id: Date.now(),
text,
completed: false
}])
}
}

const toggleTodo = (id) => {
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
))
}

const removeTodo = (id) => {
setTodos(prev => prev.filter(todo => todo.id !== id))
}

const value = { todos, addTodo, toggleTodo, removeTodo }

return (
<TodoContext.Provider value={value}>
{children}
</TodoContext.Provider>
)
}

function TodoInputContext() {
const [inputValue, setInputValue] = React.useState('')
const { addTodo } = React.useContext(TodoContext)

const handleAddTodo = () => {
addTodo(inputValue)
setInputValue('')
}

return (
<div>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={handleAddTodo}>Add</button>
</div>
)
}

function TodoListContext() {
const { todos, toggleTodo, removeTodo } = React.useContext(TodoContext)

return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
)
}

// 方案4:Zustand
import { create } from 'zustand'

const useTodoStore = create((set) => ({
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
)
})),
removeTodo: (id) => set((state) => ({
todos: state.todos.filter(todo => todo.id !== id)
}))
}))

function TodoAppWithZustand() {
const { todos, addTodo, toggleTodo, removeTodo } = useTodoStore()
const [inputValue, setInputValue] = React.useState('')

const handleAddTodo = () => {
if (inputValue.trim()) {
addTodo(inputValue)
setInputValue('')
}
}

return (
<div>
<h2>Todos (Zustand)</h2>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button onClick={handleAddTodo}>Add</button>

<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
)
}

// 综合对比页面
function StateManagementComparison() {
return (
<div>
<h1>State Management Comparison</h1>

<div style={{ marginBottom: '50px' }}>
<h3>1. useState</h3>
<TodoAppWithUseState />
</div>

<div style={{ marginBottom: '50px' }}>
<h3>2. useReducer</h3>
<TodoAppWithUseReducer />
</div>

<div style={{ marginBottom: '50px' }}>
<h3>3. Context API</h3>
<TodoAppWithContext />
</div>

<div style={{ marginBottom: '50px' }}>
<h3>4. Zustand</h3>
<TodoAppWithZustand />
</div>
</div>
)
}

八、最佳实践

1. 状态管理策略

// ✅ 好的做法:分层状态管理
// 局部状态:只在一个组件内使用的状态
const [count, setCount] = useState(0)

// 提升状态:在多个子组件间共享的状态
const [theme, setTheme] = useState('light')

// 全局状态:在整个应用中共享的状态
// 使用Redux、Context API或Zustand

// ✅ 根据复杂度选择方案
// 简单状态:useState
// 复杂逻辑:useReducer
// 跨组件共享:Context API
// 大型应用:Redux Toolkit/Zustand

2. 状态结构设计

// ✅ 状态扁平化
const state = {
user: { name: '张三', age: 25 },
todos: [
{ id: 1, text: '学习React', completed: false },
{ id: 2, text: '构建应用', completed: false }
]
}

// ❌ 避免深层嵌套
const state = {
app: {
user: {
profile: {
name: '张三',
age: 25
}
},
todos: {
items: [
{ id: 1, text: '学习React', completed: false },
{ id: 2, text: '构建应用', completed: false }
]
}
}
}

3. 异步状态管理

// ✅ 使用Redux Toolkit的createAsyncThunk
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'

// 异步action
export const fetchUser = createAsyncThunk('user/fetchUser', async (userId) => {
const response = await fetch(`https://api.example.com/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
})
}
})

4. 性能优化

// ✅ 使用React.memo避免不必要的重渲染
const TodoItem = React.memo(function TodoItem({ todo, onToggle }) {
console.log('TodoItem rendered')
return (
<li>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
</li>
)
})

// ✅ 使用useCallback缓存函数
const TodoList = React.memo(function TodoList({ todos, onToggle }) {
const handleToggle = React.useCallback((id) => {
onToggle(id)
}, [onToggle])

return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={handleToggle}
/>
))}
</ul>
)
})

九、常见问题解答

Q1: 什么时候应该将状态提升到父组件?

应该提升的情况:

  • 多个组件需要访问同一个状态
  • 状态需要同步到多个组件
  • 状态的变化会影响多个组件

不应该提升的情况:

  • 状态只在单个组件内部使用
  • 状态的更新不需要同步到其他组件

Q2: 如何避免Context API的性能问题?

// ✅ 使用多个Context避免不必要的重渲染
const UserContext = createContext()
const ThemeContext = createContext()
const SettingsContext = createContext()

// ❌ 避免将所有状态放在一个Context中
const AppContext = createContext({
user: null,
theme: 'light',
settings: {}
})

// ✅ 拆分Context
function App() {
const [user, setUser] = useState(null)
const [theme, setTheme] = useState('light')
const [settings, setSettings] = useState({})

return (
<UserContext.Provider value={{ user, setUser }}>
<ThemeContext.Provider value={{ theme, setTheme }}>
<SettingsContext.Provider value={{ settings, setSettings }}>
{children}
</SettingsContext.Provider>
</ThemeContext.Provider>
</UserContext.Provider>
)
}

Q3: 如何在Redux中处理异步操作?

// 使用Redux Toolkit的createAsyncThunk
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'

export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
const response = await fetch('https://api.example.com/posts')
return response.json()
})

const postsSlice = createSlice({
name: 'posts',
initialState: { posts: [], loading: false, error: null },
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchPosts.pending, (state) => {
state.loading = true
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.loading = false
state.posts = action.payload
})
.addCase(fetchPosts.rejected, (state, action) => {
state.loading = false
state.error = action.error.message
})
}
})

Q4: 如何在React应用中实现撤销/重做功能?

import { useReducer, useCallback } from 'react'

const UNDO = 'UNDO'
const REDO = 'REDO'
const UPDATE_STATE = 'UPDATE_STATE'

function undoableReducer(state, action) {
const { past, present, future } = state

if (action.type === UNDO) {
const previous = past[past.length - 1]
const newPast = past.slice(0, past.length - 1)

return {
past: newPast,
present: previous,
future: [present, ...future]
}
}

if (action.type === REDO) {
const next = future[0]
const newFuture = future.slice(1)

return {
past: [...past, present],
present: next,
future: newFuture
}
}

if (action.type === UPDATE_STATE) {
return {
past: [...past, present],
present: action.payload,
future: []
}
}

return state
}

function useUndoableState(initialState) {
const [state, dispatch] = useReducer(undoableReducer, {
past: [],
present: initialState,
future: []
})

const undo = useCallback(() => {
dispatch({ type: UNDO })
}, [])

const redo = useCallback(() => {
dispatch({ type: REDO })
}, [])

const updateState = useCallback((newState) => {
dispatch({ type: UPDATE_STATE, payload: newState })
}, [])

return {
state: state.present,
past: state.past,
future: state.future,
undo,
redo,
updateState
}
}

// 使用示例
function CounterWithUndo() {
const { state: count, undo, redo, updateState } = useUndoableState(0)

const increment = () => {
updateState(count + 1)
}

return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={undo} disabled={count === 0}>Undo</button>
<button onClick={redo} disabled={false}>Redo</button>
</div>
)
}

十、总结

React状态管理是一个重要的主题,不同的方案适合不同的应用场景。通过本文的学习,你应该能够:

关键要点:

  1. 内置状态管理:适合简单组件,useStateuseReducer是基础
  2. Context API:适合跨组件共享状态,但要注意性能优化
  3. Redux:适合大型应用,提供强大的开发者工具和可预测的状态管理
  4. Redux Toolkit:Redux的现代化版本,简化了开发流程
  5. 第三方库:Zustand、Jotai等提供更简单的API和更好的开发体验

选择建议:

  • 小型应用useState + useReducer + Context API
  • 中型应用:Redux Toolkit + Context API
  • 大型应用:Redux Toolkit + 状态持久化
  • 快速开发:Zustand或Jotai

最佳实践:

  1. 状态结构扁平化:避免深层嵌套
  2. 性能优化:使用React.memo、useCallback、useMemo
  3. 错误处理:处理异步操作和错误状态
  4. 测试友好:确保状态管理逻辑可以单独测试

记住,没有最好的状态管理方案,只有最适合当前项目的方案。根据应用规模、团队熟悉度和具体需求来选择合适的技术栈。

如果你在实际开发中遇到任何问题,欢迎在评论区留言交流。祝学习愉快!🎉


最后更新:2026年5月14日
分类:React | 状态管理 | 前端开发 | JavaScript框架