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

