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

React中如何在map循环迭代时更新加载状态?

解决方案

核心问题分析

你的代码里getAnimationFrames是同步阻塞式计算——整个map循环会一次性执行完毕,期间JavaScript线程被完全占用,浏览器根本没时间处理React的DOM更新请求。所以哪怕你在回调里更新了状态,也得等整个计算结束后才会一次性渲染,中间的百分比自然看不到。

可行方案:异步分批计算

把同步的map循环拆分成异步分批执行,每次计算一小部分后让出线程,让浏览器有机会更新DOM。具体分两步改:

1. 重构getAnimationFrames为异步函数

把原来的同步map改成异步迭代,通过Promise逐步执行计算,每次迭代后调用回调并让出线程:

export const getAnimationFrames = async (
  loading?: (percentage: number) => void,
  pgn: string = defaultPgn
): Promise<AnimationFrame[]> => {
  const history = getHistoryFromPgn(pgn);
  const totalMoves = history.length;
  const animationFrames: AnimationFrame[] = [];

  for (let moveNo = 0; moveNo < totalMoves; moveNo++) {
    const move = history[moveNo];
    const animationFrame = getAnimationFrame(move);
    animationFrames.push(animationFrame);

    // 计算百分比并调用回调
    const loadingPercentage = ((moveNo + 1) * 100) / totalMoves;
    if (loading) {
      loading(loadingPercentage);
    }

    // 让出线程,让浏览器处理渲染
    await new Promise(resolve => setTimeout(resolve, 0));
  }

  return animationFrames;
};

这里用for循环替代map,每次迭代后用setTimeout(resolve, 0)让出线程,确保React有时间更新DOM。

2. 在React组件中异步调用并更新状态

使用useState管理加载百分比,在useEffect里异步调用重构后的函数,每次回调时更新状态:

import { useState, useEffect } from 'react';

export const YourComponent = () => {
  const [loadingPercentage, setLoadingPercentage] = useState(0);
  const [frames, setFrames] = useState<AnimationFrame['analysis'][]>([]);
  const [boards, setBoards] = useState<AnimationFrame['board'][]>([]);

  useEffect(() => {
    const loadFrames = async () => {
      const fullFrames = await getAnimationFrames((percentage) => {
        // 更新状态,触发DOM渲染
        setLoadingPercentage(Math.round(percentage));
      });
      // 计算完成后更新最终数据
      setFrames(fullFrames.map(frame => frame.analysis));
      setBoards(fullFrames.map(frame => frame.board));
      // 重置百分比到100
      setLoadingPercentage(100);
    };

    loadFrames();
  }, []);

  return (
    <div>
      <div>加载进度:{loadingPercentage}%</div>
      {/* 其他组件内容 */}
    </div>
  );
};

为什么之前的方案无效?

  • 方案1:把loadingPercentage加入useEffect依赖会导致useEffect重复执行,确实不合理;
  • 方案2:用loadingPercentage作为依赖会触发无限渲染,因为每次更新状态都会重新执行useEffect;
  • 方案3:useRef的更新不会触发React渲染,所以DOM不会更新;
  • 方案4:原生JS操作DOM在React中可能被React的虚拟DOM覆盖,因为React会在渲染时重新生成DOM元素。

优化点(可选)

如果觉得setTimeout(0)的间隔太频繁,可以换成requestIdleCallback,让计算在浏览器空闲时执行,避免影响页面交互:

// 替换await那一行
await new Promise(resolve => requestIdleCallback(resolve));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 18:05:38