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

React Native频繁调用API致卡顿、列表更新异常优化方案

问题根因

你遇到的列表更新不及时问题,核心原因不是请求并发阻塞,是两处代码逻辑错误:

  1. 异步调用时序错误:新增商品的点击逻辑里,没有等submitForm()的新增接口执行完成,就立刻调用了getUserProducts()拉列表。这时候后端还没完成新商品入库,拉到的自然是没包含新商品的旧数据。等你下一次操作再触发拉取时,上一次的新增早就写入完成了,就会出现“上次加的商品这次才显示”的现象。
    你现在的新增按钮逻辑问题很明显:
    onPress={() => {
      setVisible(false);
      submitForm(); // 异步函数没加await,请求发出去就直接往下走
      console.log('added');
      getUserProducts(); // 执行到这里时,新增请求大概率还没跑完
    }}
    
  2. 重复请求引发竞态:连续增删操作时短时间触发多次列表拉取请求,后发的请求可能比先发的更早返回,旧请求返回后会把新状态覆盖成旧数据,导致展示异常。
可行优化方案

按改造成本从低到高选就行,组合用效果最好:

  • 第一步先修正时序问题:所有增/删操作完成后,等接口返回成功再拉取最新列表。新增按钮的逻辑要改成async,等submitForm执行成功再调getUserProducts,删除逻辑你现在已经写了await,是对的,新增漏了await而已。
    改完的新增点击逻辑示例:
    onPress={async () => {
      setVisible(false);
      // 等待新增接口成功返回
      const addSuccess = await submitForm();
      if (addSuccess) {
        console.log('added');
        getUserProducts();
      }
    }}
    
    对应的submitForm也要补返回值,不要成功了啥也不返回:
    const submitForm = async () => {
      const body = {
        product_id: productId,
        product: product,
        num: num,
        amount: amount,
      };
      const response = await postProduct(body);
      // 新增成功返回true,失败返回false
      return response !== undefined;
    };
    
  • 第二步加请求防抖+重复请求取消:给getUserProducts加300ms左右的防抖,短时间内多次触发只会最后发一次请求,同时如果上一次拉列表的请求还没完成,就取消上一次的请求,避免旧请求返回覆盖新数据。
    最简实现可以用ref存标记位:
    // 加两个ref存请求取消控制器和防抖定时器
    const getListAbortRef = useRef<AbortController | null>(null);
    const getListTimerRef = useRef<NodeJS.Timeout | null>(null);
    
    const getUserProducts = () => {
      // 清掉之前的防抖定时器
      if (getListTimerRef.current) clearTimeout(getListTimerRef.current);
      // 取消上一次还在传输的请求
      if (getListAbortRef.current) getListAbortRef.current.abort();
    
      getListTimerRef.current = setTimeout(async () => {
        const controller = new AbortController();
        getListAbortRef.current = controller;
        try {
          const response = await axios.get(
            'http://192.168.1.32:3000/api/user_products',
            { signal: controller.signal }
          );
          setUserProducts(response.data);
        } catch (error) {
          // 忽略主动取消的请求报错
          if (!axios.isCancel(error)) {
            alert('获取列表失败');
          }
        } finally {
          getListAbortRef.current = null;
        }
      }, 300);
    };
    
  • 第三步做本地乐观更新(体验最优):增删操作根本不需要等接口返回再重新拉全量列表,操作的时候直接本地更新userProducts状态,接口在后台静默执行就行,既没有请求并发问题,用户操作也没有等待感。
    比如新增成功后直接往数组里塞新条目:
    // submitForm成功后不用拉全量列表,直接本地更新
    const newItem = { product_id: productId, product, num, amount };
    setUserProducts(prev => [...prev, newItem]);
    
    删除的时候直接过滤掉对应id的条目:
    onPress={async () => {
      // 先本地更新,用户立刻看到删除效果
      setUserProducts(prev => prev.filter(item => item.product_id !== userProduct.product_id));
      await deleteProduct(userProduct.product_id);
      ToastAndroid.show('Done', ToastAndroid.SHORT);
      // 怕本地状态和后端不一致的话,可以隔几秒后台静默拉一次同步,不用等接口返回再刷新UI
    }}
    
    这种改法完全避免了频繁拉列表的性能损耗,就算偶尔接口请求失败,加个简单的失败回滚逻辑就行——比如新增失败就把刚塞进去的条目删掉,给个错误提示,完全不影响使用。
额外注意点
  • 你初始useEffect里的console.log(userProducts)打出来的永远是初始空值,因为state更新是异步的,effect执行的时候还没拿到更新后的状态,这个log没用可以删掉。
  • 现在所有列表项的CheckBox都绑定了同一个toggleCheckBox状态,点一个所有框都会跟着变,这个也是待修复的逻辑问题,需要给每个商品单独存选中状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:27:17