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

React中API调用前后setState并发执行导致状态异常的解决方法

解决React中setState异步更新的竞态问题

问题根源

你遇到的问题本质是React的setState是异步批量更新机制:快速点击多个按钮时,updateUserAccessLoadingState被多次调用,每次读取的isUpdatingUserAccess都是更新前的旧状态快照,导致后续更新覆盖了之前的操作,最终状态出现异常。

解决方案

1. 用函数式setState修复核心问题

把直接依赖当前state的更新逻辑,改成接收最新状态的函数式更新,确保每次更新都基于最新的状态快照:

修改updateUserAccessLoadingState函数:

const updateUserAccessLoadingState = (id, loading) => {
  setIsUpdatingUserAccess(prevState => {
    const existingIndex = prevState.findIndex(el => el.requestTypeId === id);
    if (existingIndex !== -1) {
      // 复制原数组并修改对应条目(避免直接修改原状态)
      const newState = [...prevState];
      newState[existingIndex] = { ...newState[existingIndex], loading };
      return newState;
    } else {
      // 添加新的状态条目
      return [...prevState, { requestTypeId: id, loading }];
    }
  });
};

函数式更新会让React传入当前最新的prevState,彻底避免异步更新导致的状态覆盖问题。

2. 防止重复点击(优化体验+减少无效操作)

在API请求未完成时,禁止对应按钮的重复点击,避免不必要的状态更新和API调用:

修改updateRequestAccess函数:

const updateRequestAccess = (requestId, checked, user) => {
  // 检查当前id是否正在加载,是则直接返回
  const isLoading = isUpdatingUserAccess.some(el => el.requestTypeId === requestId && el.loading);
  if (isLoading) return;

  updateUserAccessLoadingState(requestId, true);
  someApi.put(requestId, user, checked).finally(() => {
    // 无论请求成功失败,都取消加载状态(用finally更简洁)
    updateUserAccessLoadingState(requestId, false);
  });
};

同时在按钮组件中根据状态禁用:

<button 
  onClick={() => updateRequestAccess(id, checked, user)}
  disabled={isUpdatingUserAccess.some(el => el.requestTypeId === id && el.loading)}
>
  {isUpdatingUserAccess.some(el => el.requestTypeId === id && el.loading) ? '加载中...' : '切换权限'}
</button>

3. 优化状态结构(可选,提升性能)

把数组形式的状态改成对象(以requestTypeId为key),查找和修改效率更高,代码更简洁:

初始化状态:

const [isUpdatingUserAccess, setIsUpdatingUserAccess] = useState({});

修改updateUserAccessLoadingState:

const updateUserAccessLoadingState = (id, loading) => {
  setIsUpdatingUserAccess(prevState => ({
    ...prevState,
    [id]: loading
  }));
};

判断加载状态时直接用:

const isLoading = isUpdatingUserAccess[requestId];

总结

优先用函数式setState解决异步更新的竞态问题,再结合按钮禁用防止重复操作,最后可以根据需求优化状态结构提升代码效率。

内容的提问来源于stack exchange,提问作者SmalliSax

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 04:35:37