Appearance
React 状态管理方案详解
本文档详细介绍 React 生态中主流的状态管理方案,包括 useReducer、Redux、MobX 和 Zustand,涵盖它们的原理、使用方法、适用场景和优缺点对比。
1. useReducer
1.1 原理
useReducer 是 React 内置的 Hook,它借鉴了 Redux 的设计理念,是 useState 的替代方案。
核心原理:
State + Action = New StateuseReducer 使用纯函数(reducer)来管理状态更新逻辑:
- Reducer 函数:接收当前 state 和 action,返回新的 state
- Dispatch 函数:发送 action 触发状态更新
- 初始状态:定义状态的初始值
工作流程:
Component → dispatch(action) → reducer(state, action) → newState → Component Re-render1.2 基础使用
jsx
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 }
case 'SET_VALUE':
return { count: action.payload }
default:
throw new Error(`Unknown action type: ${action.type}`)
}
}
function Counter() {
// useReducer(reducer, initialState)
const [state, dispatch] = useReducer(reducer, { count: 0 })
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button>
<button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
<button onClick={() => dispatch({ type: 'SET_VALUE', payload: 10 })}>
Set to 10
</button>
</div>
)
}1.3 进阶使用:复杂状态管理
jsx
import { useReducer } from 'react'
// 复杂状态结构
const initialState = {
user: null,
loading: false,
error: null,
notifications: []
}
// 复杂 reducer
function appReducer(state, action) {
switch (action.type) {
case 'FETCH_USER_START':
return { ...state, loading: true, error: null }
case 'FETCH_USER_SUCCESS':
return {
...state,
loading: false,
user: action.payload,
notifications: [
...state.notifications,
{ type: 'success', message: '用户加载成功' }
]
}
case 'FETCH_USER_ERROR':
return {
...state,
loading: false,
error: action.payload,
notifications: [
...state.notifications,
{ type: 'error', message: action.payload }
]
}
case 'CLEAR_NOTIFICATION':
return {
...state,
notifications: state.notifications.filter((_, i) => i !== action.index)
}
case 'LOGOUT':
return { ...initialState }
default:
return state
}
}
function UserProfile() {
const [state, dispatch] = useReducer(appReducer, initialState)
const fetchUser = async userId => {
dispatch({ type: 'FETCH_USER_START' })
try {
const response = await fetch(`/api/users/${userId}`)
const user = await response.json()
dispatch({ type: 'FETCH_USER_SUCCESS', payload: user })
} catch (error) {
dispatch({ type: 'FETCH_USER_ERROR', payload: error.message })
}
}
return (
<div>
{state.loading && <p>Loading...</p>}
{state.error && <p>Error: {state.error}</p>}
{state.user && <p>User: {state.user.name}</p>}
{state.notifications.map((notification, index) => (
<div key={index} className={`notification ${notification.type}`}>
{notification.message}
<button
onClick={() => dispatch({ type: 'CLEAR_NOTIFICATION', index })}
>
×
</button>
</div>
))}
<button onClick={() => fetchUser(1)}>Load User</button>
<button onClick={() => dispatch({ type: 'LOGOUT' })}>Logout</button>
</div>
)
}1.4 结合 Context 实现全局状态
jsx
import { createContext, useContext, useReducer } from 'react'
// 创建 Context
const StateContext = createContext(null)
const DispatchContext = createContext(null)
// Provider 组件
function AppProvider({ children }) {
const [state, dispatch] = useReducer(appReducer, initialState)
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
)
}
// 自定义 Hooks
function useAppState() {
const context = useContext(StateContext)
if (!context) {
throw new Error('useAppState must be used within AppProvider')
}
return context
}
function useAppDispatch() {
const context = useContext(DispatchContext)
if (!context) {
throw new Error('useAppDispatch must be used within AppProvider')
}
return context
}
// 使用示例
function UserName() {
const state = useAppState()
return <span>{state.user?.name}</span>
}
function LoginButton() {
const dispatch = useAppDispatch()
const handleLogin = () => {
dispatch({ type: 'FETCH_USER_SUCCESS', payload: { name: 'John' } })
}
return <button onClick={handleLogin}>Login</button>
}
// App 根组件
function App() {
return (
<AppProvider>
<UserName />
<LoginButton />
</AppProvider>
)
}1.5 使用场景
| 场景 | 适用性 |
|---|---|
| 组件内部复杂状态逻辑 | ✅ 非常适合 |
| 表单状态管理 | ✅ 适合 |
| 多步向导/流程控制 | ✅ 适合 |
| 全局状态管理 | ⚠️ 需配合 Context |
| 大型应用架构 | ⚠️ 需要额外封装 |
1.6 优缺点
优点:
- 内置 Hook:无需安装额外依赖
- 可预测性:状态更新逻辑集中在 reducer 中,易于追踪和调试
- 可测试性:reducer 是纯函数,易于单元测试
- 适合复杂逻辑:比 useState 更适合管理复杂的状态转换
- 性能优化:可以配合 useMemo、useCallback 优化渲染
缺点:
- 样板代码:需要编写 reducer、action types 等
- 局部作用域:默认只能在组件内部使用,跨组件共享需要 Context
- 无中间件:不支持异步操作的中间件机制
- 调试工具:相比 Redux,调试工具支持较弱
2. Redux
2.1 原理
Redux 是一个独立的 JavaScript 状态管理库,遵循单向数据流和不可变状态原则。
三大原则:
- 单一数据源:整个应用的 state 存储在一个 store 中
- State 是只读的:唯一改变 state 的方式是触发 action
- 使用纯函数修改:通过 reducer 函数处理 action 并返回新 state
核心概念:
┌─────────────────────────────────────────────────────────┐
│ Redux Store │
│ ┌─────────────────────────────────────────────────┐ │
│ │ State │ │
│ └─────────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ ┌──────────────────────┴──────────────────────────┐ │
│ │ Reducer │ │
│ │ (state, action) => newState │ │
│ └─────────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ ┌──────────────────────┴──────────────────────────┐ │
│ │ Dispatch(action) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘数据流:
Action → Middleware → Reducer → New State → UI Update2.2 Redux Toolkit 使用(推荐)
Redux Toolkit (RTK) 是官方推荐的 Redux 开发方式,大幅简化了 Redux 的使用。
安装:
bash
npm install @reduxjs/toolkit react-redux基础配置:
jsx
// store.js
import { configureStore, createSlice } from '@reduxjs/toolkit'
// 创建 slice(包含 reducer 和 actions)
const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0,
status: 'idle'
},
reducers: {
increment: state => {
state.value += 1 // RTK 使用 Immer,可以直接修改
},
decrement: state => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
reset: state => {
state.value = 0
}
},
// 异步操作使用 createAsyncThunk
extraReducers: builder => {
builder
.addCase(fetchCount.pending, state => {
state.status = 'loading'
})
.addCase(fetchCount.fulfilled, (state, action) => {
state.status = 'idle'
state.value = action.payload
})
.addCase(fetchCount.rejected, state => {
state.status = 'failed'
})
}
})
// 异步 action
export const fetchCount = createAsyncThunk(
'counter/fetchCount',
async amount => {
const response = await fetch(`/api/count?amount=${amount}`)
return response.json()
}
)
// 导出 actions
export const { increment, decrement, incrementByAmount, reset } =
counterSlice.actions
// 创建 store
export const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
})在 React 中使用:
jsx
// App.js
import { Provider } from 'react-redux'
import { store } from './store'
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
)
}
// Counter.js
import { useSelector, useDispatch } from 'react-redux'
import {
increment,
decrement,
incrementByAmount,
reset,
fetchCount
} from './store'
function Counter() {
const count = useSelector(state => state.counter.value)
const status = useSelector(state => state.counter.status)
const dispatch = useDispatch()
return (
<div>
<p>Count: {count}</p>
<p>Status: {status}</p>
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(decrement())}>-1</button>
<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
<button onClick={() => dispatch(reset())}>Reset</button>
<button onClick={() => dispatch(fetchCount(10))}>Fetch Count</button>
</div>
)
}2.3 完整项目结构示例
src/
├── store/
│ ├── index.js # store 配置
│ ├── slices/
│ │ ├── userSlice.js # 用户状态
│ │ ├── cartSlice.js # 购物车状态
│ │ └── uiSlice.js # UI 状态
│ └── middleware/
│ └── logger.js # 自定义中间件
├── hooks/
│ └── redux.js # 类型化的 hooks
└── App.jsuserSlice.js 示例:
jsx
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
// 异步登录
export const loginUser = createAsyncThunk(
'user/login',
async ({ username, password }, { rejectWithValue }) => {
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
})
if (!response.ok) {
throw new Error('Login failed')
}
return await response.json()
} catch (error) {
return rejectWithValue(error.message)
}
}
)
const userSlice = createSlice({
name: 'user',
initialState: {
user: null,
token: null,
status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'
error: null
},
reducers: {
logout: state => {
state.user = null
state.token = null
state.status = 'idle'
state.error = null
},
updateUser: (state, action) => {
state.user = { ...state.user, ...action.payload }
}
},
extraReducers: builder => {
builder
.addCase(loginUser.pending, state => {
state.status = 'loading'
state.error = null
})
.addCase(loginUser.fulfilled, (state, action) => {
state.status = 'succeeded'
state.user = action.payload.user
state.token = action.payload.token
})
.addCase(loginUser.rejected, (state, action) => {
state.status = 'failed'
state.error = action.payload
})
}
})
export const { logout, updateUser } = userSlice.actions
export default userSlice.reducerstore/index.js:
jsx
import { configureStore } from '@reduxjs/toolkit'
import userReducer from './slices/userSlice'
import cartReducer from './slices/cartSlice'
import uiReducer from './slices/uiSlice'
export const store = configureStore({
reducer: {
user: userReducer,
cart: cartReducer,
ui: uiReducer
},
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
serializableCheck: false
}).concat(loggerMiddleware),
devTools: process.env.NODE_ENV !== 'production'
})2.4 使用场景
| 场景 | 适用性 |
|---|---|
| 大型复杂应用 | ✅ 非常适合 |
| 需要严格状态管理 | ✅ 非常适合 |
| 团队协作开发 | ✅ 适合 |
| 需要时间旅行调试 | ✅ 非常适合 |
| 服务端渲染 | ✅ 适合 |
| 小型简单应用 | ❌ 过于复杂 |
2.5 优缺点
优点:
- 可预测性:单向数据流,状态变化可追踪
- 调试工具强大:Redux DevTools 支持时间旅行调试
- 中间件生态:丰富的中间件支持(redux-thunk、redux-saga 等)
- 社区成熟:大量文档、教程和解决方案
- 服务端渲染:良好的 SSR 支持
- 代码规范:强制性的代码结构,利于团队协作
缺点:
- 样板代码多:即使使用 RTK,仍有一定样板代码
- 学习曲线:概念较多,新手需要时间理解
- 过度设计:小型项目使用 Redux 可能过度
- 性能考量:需要合理使用 selector 避免不必要的渲染
3. MobX
3.1 原理
MobX 采用响应式编程范式,通过可观察状态(Observable State)和自动追踪依赖实现状态管理。
核心概念:
- Observable State(可观察状态):被 MobX 追踪的状态
- Computed Values(计算值):派生状态,自动缓存
- Reactions(反应):状态变化时的副作用
- Actions(动作):修改状态的方法
工作原理:
┌─────────────────────────────────────────────────────────┐
│ MobX 响应式系统 │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Observable │ │ Computed │ │
│ │ State │─────▶│ Value │ │
│ └──────────────┘ └──────────────┘ │
│ │ │ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Reactions │ │
│ │ (自动更新 UI / 执行副作用) │ │
│ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘依赖追踪机制: MobX 在组件渲染时自动追踪使用的 observable 属性,当这些属性变化时,组件自动重新渲染。
3.2 安装
bash
npm install mobx mobx-react-lite3.3 基础使用
jsx
import { makeAutoObservable } from 'mobx'
import { observer } from 'mobx-react-lite'
// Store 类
class CounterStore {
count = 0
constructor() {
// 自动将所有属性和方法转换为 observable/action
makeAutoObservable(this)
}
// Actions
increment() {
this.count += 1
}
decrement() {
this.count -= 1
}
reset() {
this.count = 0
}
// Computed Values(计算属性)
get doubleCount() {
return this.count * 2
}
get isPositive() {
return this.count > 0
}
}
// 创建 store 实例
const counterStore = new CounterStore()
// 观察者组件
const Counter = observer(() => {
return (
<div>
<p>Count: {counterStore.count}</p>
<p>Double: {counterStore.doubleCount}</p>
<p>Is Positive: {counterStore.isPositive ? 'Yes' : 'No'}</p>
<button onClick={() => counterStore.increment()}>+1</button>
<button onClick={() => counterStore.decrement()}>-1</button>
<button onClick={() => counterStore.reset()}>Reset</button>
</div>
)
})3.4 完整应用示例
jsx
import { makeAutoObservable, runInAction } from 'mobx'
import { observer } from 'mobx-react-lite'
import { createContext, useContext } from 'react'
// ==================== Stores ====================
// 用户 Store
class UserStore {
user = null
token = localStorage.getItem('token')
loading = false
error = null
constructor() {
makeAutoObservable(this)
}
get isLoggedIn() {
return !!this.token && !!this.user
}
async login(username, password) {
this.loading = true
this.error = null
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
})
const data = await response.json()
// 异步操作中使用 runInAction
runInAction(() => {
this.user = data.user
this.token = data.token
this.loading = false
localStorage.setItem('token', data.token)
})
} catch (error) {
runInAction(() => {
this.error = error.message
this.loading = false
})
}
}
logout() {
this.user = null
this.token = null
localStorage.removeItem('token')
}
updateUser(updates) {
this.user = { ...this.user, ...updates }
}
}
// 购物车 Store
class CartStore {
items = []
constructor() {
makeAutoObservable(this)
}
get totalItems() {
return this.items.reduce((sum, item) => sum + item.quantity, 0)
}
get totalPrice() {
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
get hasItems() {
return this.items.length > 0
}
addItem(product) {
const existingItem = this.items.find(item => item.id === product.id)
if (existingItem) {
existingItem.quantity += 1
} else {
this.items.push({ ...product, quantity: 1 })
}
}
removeItem(productId) {
this.items = this.items.filter(item => item.id !== productId)
}
updateQuantity(productId, quantity) {
const item = this.items.find(item => item.id === productId)
if (item) {
if (quantity <= 0) {
this.removeItem(productId)
} else {
item.quantity = quantity
}
}
}
clearCart() {
this.items = []
}
}
// Root Store(组合所有 stores)
class RootStore {
constructor() {
this.userStore = new UserStore()
this.cartStore = new CartStore()
}
}
// ==================== Context Setup ====================
const StoreContext = createContext(null)
function StoreProvider({ children }) {
const store = new RootStore()
return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
}
function useStore() {
const store = useContext(StoreContext)
if (!store) {
throw new Error('useStore must be used within StoreProvider')
}
return store
}
// ==================== Components ====================
// 登录表单
const LoginForm = observer(() => {
const { userStore } = useStore()
const handleSubmit = e => {
e.preventDefault()
const formData = new FormData(e.target)
userStore.login(formData.get('username'), formData.get('password'))
}
if (userStore.loading) {
return <p>Logging in...</p>
}
return (
<form onSubmit={handleSubmit}>
{userStore.error && <p style={{ color: 'red' }}>{userStore.error}</p>}
<input name='username' placeholder='Username' />
<input name='password' type='password' placeholder='Password' />
<button type='submit'>Login</button>
</form>
)
})
// 用户信息
const UserInfo = observer(() => {
const { userStore, cartStore } = useStore()
if (!userStore.isLoggedIn) {
return <LoginForm />
}
return (
<div>
<p>Welcome, {userStore.user?.name}!</p>
<p>Cart Items: {cartStore.totalItems}</p>
<button onClick={() => userStore.logout()}>Logout</button>
</div>
)
})
// 购物车
const Cart = observer(() => {
const { cartStore } = useStore()
if (!cartStore.hasItems) {
return <p>Your cart is empty</p>
}
return (
<div>
<h3>Shopping Cart</h3>
{cartStore.items.map(item => (
<div key={item.id}>
<span>{item.name}</span>
<span> x {item.quantity}</span>
<span> = ${item.price * item.quantity}</span>
<button onClick={() => cartStore.removeItem(item.id)}>Remove</button>
</div>
))}
<p>Total: ${cartStore.totalPrice}</p>
<button onClick={() => cartStore.clearCart()}>Clear Cart</button>
</div>
)
})
// 商品列表
const ProductList = observer(() => {
const { cartStore } = useStore()
const products = [
{ id: 1, name: 'Product A', price: 10 },
{ id: 2, name: 'Product B', price: 20 },
{ id: 3, name: 'Product C', price: 30 }
]
return (
<div>
<h3>Products</h3>
{products.map(product => (
<div key={product.id}>
<span>
{product.name} - ${product.price}
</span>
<button onClick={() => cartStore.addItem(product)}>
Add to Cart
</button>
</div>
))}
</div>
)
})
// ==================== App ====================
function App() {
return (
<StoreProvider>
<UserInfo />
<ProductList />
<Cart />
</StoreProvider>
)
}3.5 MobX 核心概念详解
jsx
import {
makeAutoObservable,
makeObservable,
observable,
action,
computed,
autorun,
reaction,
when,
runInAction
} from 'mobx'
// 方式一:makeAutoObservable(推荐)
class Store1 {
value = 0
constructor() {
makeAutoObservable(this)
}
}
// 方式二:makeObservable(更细粒度控制)
class Store2 {
value = 0
constructor() {
makeObservable(this, {
value: observable,
increment: action,
doubleValue: computed
})
}
increment() {
this.value += 1
}
get doubleValue() {
return this.value * 2
}
}
// Reactions 示例
const store = new Store1()
// autorun: 立即执行,依赖变化时重新执行
autorun(() => {
console.log('Value changed:', store.value)
})
// reaction: 更精细的控制,可指定追踪什么和执行什么
reaction(
() => store.value, // 追踪的数据
(value, prevValue) => {
// 变化时执行的副作用
console.log(`Value changed from ${prevValue} to ${value}`)
}
)
// when: 条件满足时执行一次
when(
() => store.value > 10, // 条件
() => {
console.log('Value is greater than 10!')
}
)
// 异步操作处理
class AsyncStore {
data = null
loading = false
constructor() {
makeAutoObservable(this)
}
async fetchData() {
this.loading = true
const response = await fetch('/api/data')
const data = await response.json()
// 方式一:runInAction
runInAction(() => {
this.data = data
this.loading = false
})
}
// 方式二:使用 flow(推荐用于复杂异步)
*fetchDataWithFlow() {
this.loading = true
try {
const response = yield fetch('/api/data')
const data = yield response.json()
this.data = data
this.loading = false
} catch (error) {
this.loading = false
throw error
}
}
}3.6 使用场景
| 场景 | 适用性 |
|---|---|
| 面向对象编程风格 | ✅ 非常适合 |
| 复杂领域模型 | ✅ 非常适合 |
| 需要细粒度响应 | ✅ 非常适合 |
| 快速原型开发 | ✅ 适合 |
| 需要自动依赖追踪 | ✅ 非常适合 |
| 函数式编程偏好 | ❌ 不太适合 |
3.7 优缺点
优点:
- 编写直观:可直接修改状态,无需 reducer/action
- 自动依赖追踪:无需手动声明依赖关系
- 细粒度响应:只有真正使用的状态变化才触发更新
- 面向对象:适合 OOP 思维的开发者
- 计算值缓存:computed 自动缓存,性能优化
- 样板代码少:相比 Redux,代码更简洁
缺点:
- 魔法行为:响应式系统有一定"魔法",调试需要理解原理
- 可变状态:与 React 不可变理念不同,可能造成困惑
- 调试工具:调试工具不如 Redux DevTools 强大
- 学习曲线:需要理解 Observable、Reaction 等概念
- TypeScript 支持:虽然支持,但配置相对复杂
4. Zustand
4.1 原理
Zustand 是一个极简的状态管理库,基于 Hooks 和 发布-订阅模式。
核心原理:
- Store:一个包含 state 和 actions 的对象
- Selector:选择性地订阅状态的一部分
- 订阅机制:状态变化时通知订阅者
工作流程:
┌─────────────────────────────────────────────────────────┐
│ Zustand Store │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ State │ │
│ │ { count: 0, user: null, ... } │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Listeners (订阅者) │ │
│ │ [component1, component2, ...] │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ setState(newState) → 通知所有订阅者 │
│ │
└─────────────────────────────────────────────────────────┘特点:
- 无需 Provider 包裹
- 基于 Hooks,使用简单
- 极小的包体积(~1KB)
- 支持中间件
4.2 安装
bash
npm install zustand4.3 基础使用
jsx
import { create } from 'zustand'
// 创建 store
const useCounterStore = create((set, get) => ({
// State
count: 0,
// Actions
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 })),
incrementByAmount: amount =>
set(state => ({
count: state.count + amount
})),
reset: () => set({ count: 0 }),
// 使用 get() 获取当前状态
logCurrentCount: () => {
console.log('Current count:', get().count)
}
}))
// 使用组件
function Counter() {
// 选择性订阅(只有 count 变化时才重新渲染)
const count = useCounterStore(state => state.count)
const { increment, decrement, reset } = useCounterStore()
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
<button onClick={reset}>Reset</button>
</div>
)
}4.4 完整应用示例
jsx
import { create } from 'zustand'
import { persist, devtools, immer } from 'zustand/middleware'
// ==================== User Store ====================
const useUserStore = create(
devtools(
persist(
(set, get) => ({
user: null,
token: null,
loading: false,
error: null,
get isLoggedIn() {
return !!get().token && !!get().user
},
login: async (username, password) => {
set({ loading: true, error: null })
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
})
const data = await response.json()
set({
user: data.user,
token: data.token,
loading: false
})
} catch (error) {
set({ error: error.message, loading: false })
}
},
logout: () => {
set({ user: null, token: null })
},
updateUser: updates => {
set(state => ({
user: { ...state.user, ...updates }
}))
}
}),
{
name: 'user-storage', // localStorage key
partialize: state => ({ token: state.token }) // 只持久化 token
}
),
{ name: 'UserStore' } // DevTools name
)
)
// ==================== Cart Store ====================
const useCartStore = create(
devtools(
immer((set, get) => ({
items: [],
get totalItems() {
return get().items.reduce((sum, item) => sum + item.quantity, 0)
},
get totalPrice() {
return get().items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
)
},
addItem: product => {
set(state => {
const existingItem = state.items.find(item => item.id === product.id)
if (existingItem) {
existingItem.quantity += 1
} else {
state.items.push({ ...product, quantity: 1 })
}
})
},
removeItem: productId => {
set(state => {
state.items = state.items.filter(item => item.id !== productId)
})
},
updateQuantity: (productId, quantity) => {
set(state => {
const item = state.items.find(item => item.id === productId)
if (item) {
if (quantity <= 0) {
state.items = state.items.filter(i => i.id !== productId)
} else {
item.quantity = quantity
}
}
})
},
clearCart: () => {
set({ items: [] })
}
})),
{ name: 'CartStore' }
)
)
// ==================== UI Store ====================
const useUIStore = create(set => ({
theme: 'light',
sidebarOpen: false,
notifications: [],
toggleTheme: () =>
set(state => ({
theme: state.theme === 'light' ? 'dark' : 'light'
})),
toggleSidebar: () =>
set(state => ({
sidebarOpen: !state.sidebarOpen
})),
addNotification: notification =>
set(state => ({
notifications: [
...state.notifications,
{ id: Date.now(), ...notification }
]
})),
removeNotification: id =>
set(state => ({
notifications: state.notifications.filter(n => n.id !== id)
}))
}))
// ==================== Components ====================
function LoginButton() {
const login = useUserStore(state => state.login)
const loading = useUserStore(state => state.loading)
const handleLogin = () => {
login('demo', 'password')
}
return (
<button onClick={handleLogin} disabled={loading}>
{loading ? 'Logging in...' : 'Login'}
</button>
)
}
function UserGreeting() {
const user = useUserStore(state => state.user)
const logout = useUserStore(state => state.logout)
if (!user) {
return <LoginButton />
}
return (
<div>
<span>Welcome, {user.name}!</span>
<button onClick={logout}>Logout</button>
</div>
)
}
function CartIcon() {
const totalItems = useCartStore(state => state.totalItems)
return <div>🛒 {totalItems}</div>
}
function Cart() {
const items = useCartStore(state => state.items)
const totalPrice = useCartStore(state => state.totalPrice)
const addItem = useCartStore(state => state.addItem)
const removeItem = useCartStore(state => state.removeItem)
const clearCart = useCartStore(state => state.clearCart)
const products = [
{ id: 1, name: 'Product A', price: 10 },
{ id: 2, name: 'Product B', price: 20 }
]
return (
<div>
<h3>Cart</h3>
{/* Products */}
{products.map(product => (
<button key={product.id} onClick={() => addItem(product)}>
Add {product.name}
</button>
))}
{/* Cart Items */}
{items.map(item => (
<div key={item.id}>
{item.name} x {item.quantity} = ${item.price * item.quantity}
<button onClick={() => removeItem(item.id)}>Remove</button>
</div>
))}
<p>Total: ${totalPrice}</p>
<button onClick={clearCart}>Clear Cart</button>
</div>
)
}
// ==================== App ====================
function App() {
return (
<div>
<UserGreeting />
<CartIcon />
<Cart />
</div>
)
}4.5 Zustand 中间件
jsx
import { create } from 'zustand'
import {
persist, // 持久化
devtools, // Redux DevTools 集成
immer, // Immer 集成
subscribeWithSelector, // 订阅特定状态变化
combine // 合并多个 store
} from 'zustand/middleware'
// 1. persist - 持久化到 localStorage/sessionStorage
const usePersistedStore = create(
persist(
set => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 }))
}),
{
name: 'my-storage', // unique name
storage: localStorage, // or sessionStorage
partialize: state => ({ count: state.count }) // 选择性持久化
}
)
)
// 2. devtools - Redux DevTools 支持
const useDevtoolsStore = create(
devtools(
set => ({
count: 0,
increment: () => set({ count: 1 }, false, 'increment') // action name
}),
{ name: 'MyStore' }
)
)
// 3. immer - 使用 Immer 进行不可变更新
const useImmerStore = create(
immer(set => ({
user: { name: 'John', age: 30 },
updateAge: () =>
set(state => {
state.user.age += 1 // 直接修改
})
}))
)
// 4. subscribeWithSelector - 订阅特定状态变化
const useSubscribeStore = create(
subscribeWithSelector((set, get) => ({
count: 0,
increment: () => set({ count: get().count + 1 })
}))
)
// 在组件外部订阅
useSubscribeStore.subscribe(
state => state.count, // selector
count => {
// callback
console.log('Count changed:', count)
}
)
// 5. 组合多个中间件
const useCombinedStore = create(
devtools(
persist(
immer(set => ({
count: 0,
increment: () =>
set(state => {
state.count += 1
})
})),
{ name: 'combined-storage' }
),
{ name: 'CombinedStore' }
)
)4.6 高级用法
jsx
import { create, useStore } from 'zustand'
import { createContext, useContext } from 'react'
// ==================== 模式一:Context 模式(用于测试或多实例) ====================
const StoreContext = createContext(null)
const createStore = () =>
create(set => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 }))
}))
function StoreProvider({ children }) {
const store = createStore()
return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
}
function useBoundStore(selector) {
const store = useContext(StoreContext)
return useStore(store, selector)
}
// ==================== 模式二:Slice 模式(模块化) ====================
const createUserSlice = (set, get) => ({
user: null,
setUser: user => set({ user }),
clearUser: () => set({ user: null })
})
const createCartSlice = (set, get) => ({
items: [],
addItem: item =>
set(state => ({
items: [...state.items, item]
})),
clearCart: () => set({ items: [] })
})
const useBoundStore = create((...a) => ({
...createUserSlice(...a),
...createCartSlice(...a)
}))
// ==================== 模式三:异步 Actions ====================
const useAsyncStore = create(set => ({
data: null,
loading: false,
error: null,
fetchData: async id => {
set({ loading: true, error: null })
try {
const response = await fetch(`/api/data/${id}`)
const data = await response.json()
set({ data, loading: false })
} catch (error) {
set({ error: error.message, loading: false })
}
}
}))
// ==================== 模式四:Computed Values ====================
const useComputedStore = create((set, get) => ({
items: [],
// 计算属性需要在 selector 中实现
addItem: item =>
set(state => ({
items: [...state.items, item]
}))
}))
// 使用 selector 实现计算属性
const useTotalItems = () =>
useComputedStore(state =>
state.items.reduce((sum, item) => sum + item.quantity, 0)
)
// 或者使用自定义 hook
const useCartTotals = () => {
const items = useComputedStore(state => state.items)
return {
totalItems: items.reduce((sum, item) => sum + item.quantity, 0),
totalPrice: items.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
}4.7 使用场景
| 场景 | 适用性 |
|---|---|
| 中小型应用 | ✅ 非常适合 |
| 快速原型开发 | ✅ 非常适合 |
| 需要简洁代码 | ✅ 非常适合 |
| 需要持久化 | ✅ 内置支持 |
| 需要多 store | ✅ 支持 |
| 超大型复杂应用 | ⚠️ 需要良好组织 |
4.8 优缺点
优点:
- 极简 API:学习成本低,上手快
- 无 Provider:不需要包裹 Provider 组件
- 体积小:仅约 1KB gzipped
- TypeScript 友好:类型推断完善
- 选择性订阅:精确控制组件渲染
- 中间件支持:persist、devtools、immer 等
- React 18 兼容:支持并发渲染
缺点:
- 调试工具:需要配合 Redux DevTools
- 时间旅行:不如 Redux 原生支持完善
- 社区生态:相比 Redux,生态较小
- 大型项目:需要自行组织代码结构
5. 方案对比总结
5.1 核心特性对比
| 特性 | useReducer | Redux | MobX | Zustand |
|---|---|---|---|---|
| 学习曲线 | 低 | 中高 | 中 | 低 |
| 样板代码 | 中 | 高 | 低 | 低 |
| 包体积 | 0 (内置) | ~7KB | ~16KB | ~1KB |
| 调试工具 | 基础 | 优秀 | 良好 | 良好 |
| TypeScript | ✅ | ✅ | ✅ | ✅ |
| 中间件 | ❌ | ✅ | ✅ | ✅ |
| 时间旅行 | ❌ | ✅ | ⚠️ | ⚠️ |
| SSR 支持 | ✅ | ✅ | ✅ | ✅ |
| React 18 | ✅ | ✅ | ✅ | ✅ |
5.2 适用场景对比
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 组件内部复杂状态 | useReducer | 无需额外依赖,逻辑集中 |
| 小型项目 (< 10 组件) | Zustand / useReducer | 简单直接,无需复杂架构 |
| 中型项目 (10-50 组件) | Zustand / MobX | 平衡简洁性和功能性 |
| 大型项目 (> 50 组件) | Redux / MobX | 成熟的架构和工具链 |
| 团队协作 | Redux | 强制性的代码结构 |
| 快速原型 | Zustand / MobX | 样板代码少 |
| 需要严格状态追踪 | Redux | 单向数据流,可预测 |
| 面向对象偏好 | MobX | 类和装饰器风格 |
| 函数式偏好 | Redux / Zustand | 纯函数和不可变数据 |
5.3 性能对比
| 方案 | 渲染优化方式 | 性能特点 |
|---|---|---|
| useReducer | Context + useMemo | 需要手动优化,Context 可能导致不必要渲染 |
| Redux | useSelector | 选择性订阅,性能良好 |
| MobX | observer + 自动追踪 | 细粒度响应,自动优化 |
| Zustand | selector | 选择性订阅,性能优秀 |
5.4 选型建议
项目规模
│
├── 小型项目 (< 10 组件)
│ └── useReducer + Context 或 Zustand
│
├── 中型项目 (10-50 组件)
│ ├── 需要快速开发 → Zustand 或 MobX
│ └── 需要规范架构 → Redux Toolkit
│
└── 大型项目 (> 50 组件)
├── 团队经验丰富 → Redux Toolkit
├── OOP 风格偏好 → MobX
└── 追求简洁 → Zustand5.5 迁移建议
| 从 | 到 | 难度 | 建议 |
|---|---|---|---|
| useState | useReducer | 低 | 适合状态逻辑变复杂时 |
| useReducer | Redux | 中 | 需要全局状态时 |
| Redux | Zustand | 中 | 简化代码,减少样板 |
| MobX | Zustand | 中 | 函数式风格偏好 |
| Redux | MobX | 高 | 需要重写状态逻辑 |
总结
选择状态管理方案时,应考虑以下因素:
- 项目规模:小型项目优先选择简单方案
- 团队经验:选择团队熟悉的方案
- 性能需求:考虑渲染优化和包体积
- 开发效率:平衡样板代码和开发速度
- 维护成本:考虑长期维护和扩展
没有绝对最好的方案,只有最适合当前项目需求的方案。建议在项目初期选择简单的方案,随着需求复杂度增加再考虑升级或迁移。