【React】 状态管理方案

以下文章我们通过电商这个场景来详细聊聊 React 中的状态管理方案。

为什么需要状态管理?

你以前写前端,每个组件自己管自己的 state,用 useState 就够了。但电商场景里经常出现这样的情况:

复制代码
购物车组件(显示数量)
    ↑
商品详情页组件(点"加入购物车")
    ↑
Header 组件(右上角购物车图标小红点)

这三个组件必须共享同一份「购物车数据」。如果各自管各自的,A 加了商品 B 不知道,这就炸了。

状态管理就是为了解决「多组件之间共享和同步数据」这个问题的。


一、useContext ------ React 自带的"全局变量"

本质

React 内置的发布-订阅 机制。用 createContext 创建一个全局盒子,Provider 往盒子里放数据,任何子组件通过 useContext 取数据。

怎么工作的

jsx 复制代码
// Step 1: 创建盒子
const CartContext = createContext()

// Step 2: 顶层用 Provider 包裹,value 就是共享的数据
function App() {
  const [items, setItems] = useState([])
  return (
    <CartContext.Provider value={{ items, setItems }}>
      <CartIcon />    {/* 小红点 */}
      <ProductPage /> {/* 加购按钮 */}
    </CartContext.Provider>
  )
}

// Step 3: 任何子组件都能直接取
function CartIcon() {
  const { items } = useContext(CartContext)
  return <span>{items.length}</span>
}

最大的坑:性能问题

context value 一变,所有用了这个 context 的组件全部重新渲染,不管它们用到的字段是否变了。

复制代码
items 从 [] 变成 [{id:1}]
    ↓
CartContext.Provider 的 value 变了
    ↓
CartIcon ???   ProductPage ???   ProductList ???   Footer ???
    ↓              ↓                ↓                ↓
 全都重渲染!    重渲染!         重渲染!          重渲染!

所以 useContext 适合很小的项目,或者低频变化的数据(主题、语言)。电商购物车这种频繁增删的场景,用 useContext 会带来大量不必要的重渲染。


二、Redux ------ 开山鼻祖,生态最强

本质

单向数据流 + 全局 Store + 纯函数 Reducer。 状态不能直接改,必须通过 dispatch(action) → reducer 处理 → 生成新 state。

核心模型

复制代码
Component                     Redux Store
   │                              │
   │  dispatch({type:'cart/add'}) │
   ├─────────────────────────────>│
   │                              ├── Reducer 处理
   │                              │    ↓
   │                              │  新 state
   │  useSelector(s => s.cart)    │
   │<─────────────────────────────│
   │  只取需要的字段,只在这些字段变了才重渲染

关键设计思路

Redux 要求你通过 action 描述「发生了什么」,而不是直接说「状态变成什么」。这种设计来自 Flux 架构:

  • action = 事件描述({type: "cart/add", payload: {id:1, name:"iPhone"}}
  • reducer = 纯函数(接收旧 state + action,返回新 state,不产生副作用)
  • dispatch = 触发更新的唯一入口
  • selector = 从 store 里取一段数据

性能和样板代码的平衡

Redux 的问题------样板代码太多。加一个功能要写:action type 常量 → action creator → reducer case → 挂 slice → 组件里 dispatch。RTK(Redux Toolkit)大幅简化了这个,但样板还是比 Zustand 多。

不过好处是:可预测性强。每一个 state 变化都能通过 Redux DevTools 追踪到是谁发的 action 。


三、Zustand ------ 极简主义的 Redux 替代品

本质

一个基于 hooks、零样板代码的状态管理库。你定义一个 store,返回的就是一个 hook,直接在组件里用。

为什么它比 Redux 简洁

对比代码量就明白了------Redux 要:createSliceconfigureStoreProvideruseSelector + useDispatch。Zustand 一步到位:

js 复制代码
// 定义 store(一步到位)
const useCart = create((set) => ({
  items: [],
  total: 0,
  addItem: (item) =>
    set((state) => ({
      items: [...state.items, item],
      total: state.total + item.price
    })),
  clearCart: () => set({ items: [], total: 0 })
}))

// 组件里直接用
function CartIcon() {
  const items = useCart((s) => s.items)  // 只订阅 items,items 不变就不重渲染
  const total = useCart((s) => s.total)
  return <div>{items.length} 件, 共 {total} 元</div>
}

精髓:selector 机制

useCart(s => s.items) 这行是关键------通过选择器精确订阅某个字段 ,只有这个字段变了组件才重新渲染。性能上和 Redux 的 useSelector 一样好,但写法比 Redux 少了一半代码。

和 Redux 的本质区别

Redux Zustand
核心理念 通过 action 描述意图,reducer 纯函数处理 简单的 set 更新,更接近 setState
心智模型 「事件溯源」式的 CQRS 「增强版 useState」
样板代码 多(即使有 RTK) 极少
学习曲线 中高(action/reducer/dispatch/selector/middleware...) 低(一个 create 一把梭)

四、MobX ------ 响应式编程,像 Vue 一样

本质

基于「可观察数据 + 自动追踪依赖」的响应式方案。 你定义的数据是 @observable 的,谁用了这些数据,MobX 自动帮你追踪,数据变了自动通知它们更新。

和前面三个最大的不同

前面三个(Context、Redux、Zustand)都是 「手动订阅」模式------你得告诉框架"我要看哪些数据"。MobX 是「自动追踪」 ------你只管用,它自己知道谁依赖了谁。

js 复制代码
class CartStore {
  @observable items = []

  @computed get itemCount() {
    return this.items.length    // 自动追踪依赖
  }

  @action
  addItem(item) {
    this.items.push(item)       // 直接修改,不用 dispatch 也不用 set
  }
}

// 组件里
@observer  // 这个 HOC 让组件自动追踪它用到了哪些 observable
class CartIcon extends Component {
  render() {
    return <span>{this.props.cart.itemCount}</span>
    // ↑ MobX 发现 component 用了 itemCount
    //   itemCount 依赖 items
    //   items 变了 → 自动重渲染这个组件
  }
}

这写法和 Vue 的 reactive/computed 几乎一模一样------如果你学过 Vue,MobX 对你来说最自然。

优缺点

  • 优点:代码最少 (直接 this.items.push),自动追踪依赖不需要手动选
  • 缺点:"魔法感"太强------你不知道它什么时候更新,追踪链路是隐式的,出了问题不好调试。另外装饰器语法和 React Hooks 的哲学不太一致。

全方位对比表

维度 useContext Redux (RTK) Zustand MobX
数据流 发布-订阅 单向 + dispatch 单向 + set 响应式自动追踪
修改状态 setState dispatch(action) set() 直接修改属性
细粒度订阅 不支持 useSelector 选择器 自动追踪
样板代码 极少 中等 极少
性能 差(全量重渲染)
包体积 0(内置) ~12KB ~2KB ~16KB
中间件/插件 极强(redux-saga, thunk...) 有(精简)
学习曲线 中-高
相关推荐
用户2136610035721 小时前
Vue商品详情与放大镜组件
前端·javascript
半个落月1 小时前
从Tapas小Demo理清localStorage、事件与this
前端·javascript
李明卫杭州1 小时前
Vue2 中 v-model 处理不同数据结构的技巧
前端·javascript·vue.js
李明卫杭州1 小时前
使用 computed 处理 v-model 复杂数据结构
前端·javascript·vue.js
丨我是张先生丨2 小时前
日语单词 Web Page
前端·css·css3
禅思院3 小时前
AI对话前端从入门到崩溃:一个长对话引发的五层优化战争【引子】
前端·面试·架构
TrisighT4 小时前
Electron 鸿蒙 PC 上点外链唤醒应用,我试了 6 种写法只有 1 种能跑
前端·electron·harmonyos
2501_930707784 小时前
如何将HTML文件转换为纯文本(详细步骤指南)
前端·html
天才熊猫君5 小时前
配置与数据分离:一种可视化搭建的属性编辑方案
前端·javascript