You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React状态管理是否必须使用Reducer?求带源码的错误对比与修复示例

React状态管理是否必须使用Reducer?求带源码的错误对比与修复示例

兄弟我太懂你的困惑了!一堆文章都说复杂React应用得用Reducer(Redux、useReducer这类),但就是拿不出直观的“有bug vs 修复后”的代码对比,看得人抓心挠肝。今天咱们就拿购物车状态管理这个最常见的场景来拆解,直接看代码、看问题、看修复效果!

场景说明

假设我们做一个简单购物车:可以添加商品、修改商品数量,当用户快速连续操作(比如狂点“添加”或者同时调整数量)时,不用Reducer很容易出现状态计算错误。

错误实现:只用useState,状态更新异常

先看不用Reducer的代码,这里用useState管理购物车列表:

import { useState } from 'react';

function Cart() {
  const [cartItems, setCartItems] = useState([]);

  // 添加商品到购物车
  const addItem = (itemId) => {
    // 查找商品是否已存在
    const existingItem = cartItems.find(item => item.id === itemId);
    if (existingItem) {
      // 存在则数量+1
      setCartItems(
        cartItems.map(item => 
          item.id === itemId ? {...item, quantity: item.quantity + 1} : item
        )
      );
    } else {
      // 不存在则新增
      setCartItems([...cartItems, {id: itemId, quantity: 1}]);
    }
  };

  // 减少商品数量
  const decreaseQuantity = (itemId) => {
    const existingItem = cartItems.find(item => item.id === itemId);
    if (existingItem.quantity === 1) {
      // 数量为1则删除商品
      setCartItems(cartItems.filter(item => item.id !== itemId));
    } else {
      // 否则数量-1
      setCartItems(
        cartItems.map(item => 
          item.id === itemId ? {...item, quantity: item.quantity - 1} : item
        )
      );
    }
  };

  return (
    <div>
      <button onClick={() => addItem(1)}>添加商品1</button>
      <button onClick={() => decreaseQuantity(1)}>减少商品1数量</button>
      <div>购物车:{JSON.stringify(cartItems)}</div>
    </div>
  );
}

export default Cart;

问题在哪?

当你快速连续点击“添加商品1”按钮(比如1秒点5次),会发现最终的数量经常不对——比如点了5次,结果数量只变成3或者4。

原因很简单:useState的更新是异步批量处理的,每次调用setCartItems时,cartItems拿到的是更新前的旧值。快速点击时,多个更新操作会基于同一个旧状态计算,导致后面的更新覆盖了前面的,最终结果不符合预期。

修复实现:用useReducer保证状态更新可靠性

现在用useReducer重构,把状态更新逻辑抽到纯函数里,每次更新都基于最新的状态:

import { useReducer } from 'react';

// 定义Reducer纯函数:接收当前状态和动作,返回新状态
const cartReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_ITEM': {
      const existingItem = state.find(item => item.id === action.payload);
      if (existingItem) {
        return state.map(item => 
          item.id === action.payload ? {...item, quantity: item.quantity + 1} : item
        );
      } else {
        return [...state, {id: action.payload, quantity: 1}];
      }
    }
    case 'DECREASE_QUANTITY': {
      const existingItem = state.find(item => item.id === action.payload);
      if (existingItem.quantity === 1) {
        return state.filter(item => item.id !== action.payload);
      } else {
        return state.map(item => 
          item.id === action.payload ? {...item, quantity: item.quantity - 1} : item
        );
      }
    }
    default:
      return state;
  }
};

function Cart() {
  const [cartItems, dispatch] = useReducer(cartReducer, []);

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

  const decreaseQuantity = (itemId) => {
    dispatch({type: 'DECREASE_QUANTITY', payload: itemId});
  };

  return (
    <div>
      <button onClick={() => addItem(1)}>添加商品1</button>
      <button onClick={() => decreaseQuantity(1)}>减少商品1数量</button>
      <div>购物车:{JSON.stringify(cartItems)}</div>
    </div>
  );
}

export default Cart;

为什么能修复?

useReducer的工作机制是:每次调用dispatch时,React会把动作加入队列,然后基于最新的状态去执行Reducer函数计算新状态。不管你点击多快,每个动作都会按顺序处理,不会再出现基于旧状态计算的问题——快速点5次添加,数量肯定会变成5,完全符合预期。

总结一下

不是所有React应用都必须用Reducer!如果你的状态更新逻辑很简单(比如只是个开关、输入框内容),用useState完全足够。但当你遇到以下场景时,Reducer能帮你避免很多隐性bug:

  • 状态更新逻辑复杂,有多个分支判断
  • 状态更新依赖之前的状态,且可能有频繁的连续操作
  • 多个组件需要修改同一状态(Reducer配合Context用起来更顺手)

备注:内容来源于stack exchange,提问作者dzbogota1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 12:02:59