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

React useMemo未获取对象数组最新状态问题排查

问题原因与解决方案

核心问题1:异步forEach导致状态更新时机错误

你用asyncProcesses.forEach(async (process, i) => { ... })处理异步操作,但forEach不会等待内部的async函数执行完成,setAsyncProcesses(newAsyncProcesses)会在所有异步请求结束前就执行,此时newAsyncProcesses里的对象还没被更新,useMemo自然拿不到最新值。

核心问题2:直接修改原状态对象(Mutation)

checkUploadCsvProcess函数里直接修改了传入的process对象(比如process.snackbarType = 'success'),这属于直接修改原状态的引用类型。虽然你复制了数组[...asyncProcesses],但数组里的对象还是原状态的引用,React依赖引用变化触发重渲染和useMemo重新计算,这种mutation操作无法让React感知到状态的真实变化,即便后续异步操作完成,也可能因引用未变导致useMemo不更新。


修复方案

步骤1:替换异步forEach为Promise.all + map

用map生成所有异步Promise,通过Promise.all等待所有操作完成后再更新状态:

// 替换原forEach逻辑
const updatedProcesses = await Promise.all(
  asyncProcesses.map(async (process) => {
    // 仅处理需要更新的process,比如判断类型为CsvUpload
    if (process.processType === AsyncProcessType.CsvUpload) {
      return checkUploadCsvProcess(
        process as AsyncProcessCsvUpload,
        {
          actionName: dictionary.review,
          actionOnClick: () => {
            navigate(routes.linkedAccounting);
            setIsOpenInDialog(AsyncProcessType.CsvUpload, true);
          },
        }
      );
    }
    // 无需更新的直接返回原对象
    return process;
  })
);

// 过滤checkUploadCsvProcess报错时返回的undefined
const newAsyncProcesses = updatedProcesses.filter(Boolean) as AsyncProcess[];
setAsyncProcesses(newAsyncProcesses);

步骤2:修改checkUploadCsvProcess,避免直接修改原对象

在函数内部创建原对象的副本,修改副本后返回,确保状态更新是不可变的:

export const checkUploadCsvProcess = async (
  process: AsyncProcessCsvUpload,
  reviewAction: {
    actionName: string,
    actionOnClick: () => void
  },
): Promise<AsyncProcessCsvUpload | undefined> => {
  try {
    const integration = await integrationsClient.getIntegration(process.data.integrationId);
    const { logs, status } = integration;

    // 创建原对象的浅拷贝(若对象层级较深,建议用structuredClone或lodash.clonedeep做深拷贝)
    const updatedProcess = { ...process, data: { ...process.data } };

    if (status === IntegrationStatus.UPLOAD) {
      updatedProcess.isActive = true;
      updatedProcess.progress = Math.round((logs?.info?.progress || 0) / (logs?.info?.transactionCount || 1) * 100);
    }
    else if (status === IntegrationStatus.ERROR) {
      updatedProcess.isActive = false;
      updatedProcess.snackbarType = 'error';
      delete updatedProcess.progress;
      updatedProcess.snackbarAction = reviewAction;
      updatedProcess.data.errors = logs?.error || {};
    }
    else {
      updatedProcess.isActive = false;
      updatedProcess.snackbarType = 'success';
      delete updatedProcess.progress;
      updatedProcess.snackbarAction = reviewAction;
    }

    updatedProcess.timesChecked++;
    return updatedProcess;
  }
  catch (error) {
    return;
  }
};

修复逻辑说明

  • Promise.all + map确保所有异步操作完成后再更新状态,保证newAsyncProcesses是最终的最新结果。
  • 创建对象副本并修改,避免了直接修改原状态的引用,确保每次状态更新都是新的引用,React能正确感知变化,进而触发useMemo重新计算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 03:15:57