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

React中IsLoading状态需保持至所有卡片DOM渲染完成的问题

问题分析与修复方案

核心问题

你的代码里finally块会在所有网络请求完成后立刻执行setIsLoading(false),但React的状态更新和DOM渲染是异步的——此时数据刚更新到状态,卡片还没渲染到DOM里,所以loading会提前关闭。另外,代码里的状态更新逻辑混乱(循环中多次调用setData、穿插过滤操作),也会导致loading状态异常触发。

修复步骤与代码示例

1. 批量获取+统一处理数据

先一次性获取所有需要的数据,处理好过滤后再更新状态,避免多次状态更新导致的混乱:

useEffect(() => {
  setIsLoading(true);
  async function fetchAllPokemonData() {
    try {
      let fetchedData = [];
      if (search) {
        // 搜索单只宝可梦
        const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${search}`);
        fetchedData = [await res.json()];
      } else {
        // 并行批量请求,提升效率(替代串行循环)
        const fetchTasks = pokemon.map(poke => fetch(poke.url).then(res => res.json()));
        fetchedData = await Promise.all(fetchTasks);
      }

      // 统一处理过滤逻辑
      if (filter) {
        fetchedData = fetchedData.filter(item => 
          item.types.some(type => type.type.name === filter)
        );
      }

      // 一次性更新状态
      setData(fetchedData);
    } catch (error) {
      console.error('获取数据失败:', error);
      setData([]);
    }
  }
  fetchAllPokemonData();
}, [pokemon, search, filter]);

2. 监听DOM渲染完成后关闭loading

新增一个useEffect监听数据变化,等待DOM渲染完成后再关闭loading:

useEffect(() => {
  // 只有当处于loading状态且已有数据时,才执行关闭逻辑
  if (isLoading && data !== undefined) {
    // requestAnimationFrame确保等待到浏览器下一次渲染完成(DOM已更新)
    const rafId = requestAnimationFrame(() => {
      setIsLoading(false);
    });
    // 组件卸载时取消回调,避免内存泄漏
    return () => cancelAnimationFrame(rafId);
  }
}, [data, isLoading]);

额外优化点说明

  • 用Promise.all并行请求替代串行for循环:原来的循环每次要等上一个请求完成才发下一个,并行请求能大幅缩短数据获取时间。
  • 移除冗余状态更新:比如搜索时的setData([])+setData([data]),直接一次赋值即可,React会合并无关的状态更新。
  • 统一过滤逻辑:原来在循环中每添加一条数据就过滤一次,现在所有数据获取完成后统一过滤,减少状态更新次数和组件渲染次数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 02:45:07