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
相关产品推荐
相关产品推荐

