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

React中map循环调用API致无限重渲染及多实例状态处理方案

解决React组件无限重渲染并为每个order_item维护独立配送计数

问题背景

遍历order_item数组时,当item2.order_meal_deliveries.length > 0会直接调用fetchData函数,而该函数会更新全局的pendingDeliveries和totalDeliveries状态,触发组件重渲染,进而再次调用fetchData形成无限循环。同时当前状态无法区分不同order_item的配送数据,需要为每个项维护独立的待配送/总配送计数。

核心解决方案

将全局状态重构为以itemId为键的对象存储每个订单项的独立数据;用useEffect替代渲染阶段直接调用API,避免重渲染触发重复请求;用useCallback缓存API函数,减少不必要的组件更新。

步骤1:重构状态定义

把原来的两个独立状态合并为一个对象,每个键对应order_item的id,值存储该项的待配送和总配送计数:

const [deliveriesData, setDeliveriesData] = useState({});

步骤2:修改fetchData函数

更新状态时仅修改当前item对应的数据,保留其他项的状态,同时用useCallback缓存函数:

const fetchData = useCallback((orderId, itemId) => {
  api.ordersDelivery(orderId, itemId)
    .then((res) => {
      const responseData = res?.data?.response || {};
      // 仅更新当前item的数据,不覆盖其他项
      setDeliveriesData(prev => ({
        ...prev,
        [itemId]: {
          pending: responseData.pending_delivery_count || 0,
          total: responseData.total_delivery_count || 0
        }
      }));
    })
    .catch((err) => console.error('获取配送数据失败:', err));
}, []);

步骤3:用useEffect触发API请求

不在渲染循环中直接调用fetchData,而是在组件挂载或依赖变化时,批量为符合条件的订单项发起请求,同时避免重复请求:

useEffect(() => {
  item.order_item.forEach(item2 => {
    // 仅当有配送记录且未获取过该item数据时发起请求
    if (item2.order_meal_deliveries.length > 0 && !deliveriesData[item2.id]) {
      fetchData(item.id, item2.id);
    }
  });
}, [item, fetchData, deliveriesData]);

步骤4:调整渲染时的状态读取

在渲染对应订单项时,从deliveriesData中根据item2.id获取专属数据,例如进度条计算:

const getProgressValue = (itemId) => {
  const data = deliveriesData[itemId];
  if (!data || data.total === 0) return "0%";
  return `${Math.round(((data.total - data.pending) / data.total) * 100)}%`;
};

// 渲染进度条时使用
<div className="progress">
  <div
    className="progress-bar"
    role="progressbar"
    aria-valuemax="100"
    style={{ width: getProgressValue(item2.id) }}
  ></div>
</div>

步骤5:优化列表Key值

将原来的key={index}替换为key={item2.id},确保列表项Key稳定,避免不必要的重渲染:

item.order_item.map((item2) => {
  // 渲染逻辑
}, []);

完整核心代码片段

状态与函数部分

import { useState, useEffect, useCallback } from 'react';

const YourComponent = ({ item }) => {
  const [deliveriesData, setDeliveriesData] = useState({});
  const [startDate, setStartDate] = useState(null);
  const minDate = new Date();

  const fetchData = useCallback((orderId, itemId) => {
    api.ordersDelivery(orderId, itemId)
      .then((res) => {
        const responseData = res?.data?.response || {};
        setDeliveriesData(prev => ({
          ...prev,
          [itemId]: {
            pending: responseData.pending_delivery_count || 0,
            total: responseData.total_delivery_count || 0
          }
        }));
      })
      .catch((err) => console.error('获取配送数据失败:', err));
  }, []);

  useEffect(() => {
    item.order_item.forEach(item2 => {
      if (item2.order_meal_deliveries.length > 0 && !deliveriesData[item2.id]) {
        fetchData(item.id, item2.id);
      }
    });
  }, [item, fetchData, deliveriesData]);

  const getProgressValue = (itemId) => {
    const data = deliveriesData[itemId];
    if (!data || data.total === 0) return "0%";
    return `${Math.round(((data.total - data.pending) / data.total) * 100)}%`;
  };

  // 其他函数:createMealDelivery、handleViewMeal、handleRenewPlan

渲染部分

return (
    <div>
      {item.order_item.map((item2) => {
        if (item2.order_meal_deliveries.length === 0) {
          return (
            <div className="col-12 card card-md mb-4" key={item2.id}>
              {/* 未开始计划的原有渲染内容 */}
            </div>
          );
        } else {
          return (
            <div className="col-12 card card-md mb-4" key={item2.id}>
              <div className="col-auto">
                <div className="row">
                  <div className="col-lg">
                    <div className="col-12 p-4">
                      {/* 原有订单信息、标题等内容 */}
                      <div className="progress">
                        <div
                          className="progress-bar"
                          role="progressbar"
                          aria-valuemax="100"
                          style={{ width: getProgressValue(item2.id) }}
                        ></div>
                      </div>
                      {/* 查看计划、续订计划按钮等内容 */}
                    </div>
                  </div>
                </div>
              </div>
            </div>
          );
        }
      })}
    </div>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:17:07