
在react应用中,当我们需要从api获取大量相关数据时,常常会遇到并发请求导致ui渲染顺序混乱的问题。这通常发生在以下两种常见误用模式中:
以下是导致问题发生的原始代码片段的关键部分:
// 原始代码中导致乱序的关键逻辑
const collectPokemon = async (limit: number) => {
axiosInstance.get(`pokemon?limit=${limit}`).then((res) => {
const data = res.data.results; // data 是一个对象数组,不是 Promise 数组
Promise.all(data).then((results) => { // Promise.all(data) 立即解析
results.map((pkmn) => {
mapPokemon(pkmn.name); // mapPokemon 返回 Promise,但这些 Promise 未被收集或等待
});
});
});
};
const mapPokemon = async (name: string) => {
axiosInstance.get(`pokemon/${name}`).then((res) => {
const data: PokemonType = res.data;
setPokedex((currentList) => [...currentList, data]); // 每次获取一个宝可梦就更新一次状态
});
};这种模式下,mapPokemon函数内部的setPokedex调用会根据各自的网络延迟独立完成,导致最终pokedex数组中的数据顺序与API返回的初始列表顺序不一致。
解决上述问题的核心在于两点:正确地协调所有异步请求,以及在所有数据准备就绪后一次性更新组件状态。这可以通过结合使用async/await语法和Promise.all来实现。
以下是优化后的代码示例:
import { useEffect, useState } from "react";
import "./App.css";
import axios from "axios";
import { PokemonType } from "./models/pokemonType";
import Pokemon from "./components/Pokemon";
function App() {
const [pokedex, setPokedex] = useState<PokemonType[] | []>([]);
const limit = 151;
const axiosInstance = axios.create({
baseURL: "https://pokeapi.co/api/v2/",
});
useEffect(() => {
// 在组件挂载时调用 collectPokemon
collectPokemon(limit);
}, []);
// 负责收集所有宝可梦数据的主函数
const collectPokemon = async (limit: number) => {
try {
// 1. 获取宝可梦列表(名称和URL)
const res = await axiosInstance.get(`pokemon?limit=${limit}`);
const pokemonList = res.data.results;
// 2. 为每个宝可梦详情创建 Promise
// mapPokemon 函数现在返回一个 Promise,Promise.all 将等待这些 Promise
const promises = pokemonList.map((pokemon: { name: string; url: string }) =>
mapPokemon(pokemon.name)
);
// 3. 等待所有宝可梦详情请求完成
const pokemons = await Promise.all(promises);
// 4. 一次性更新状态,保证顺序
setPokedex(pokemons);
} catch (error) {
console.error("Failed to collect Pokemon data:", error);
// 可以在此处添加错误处理逻辑,如设置错误状态,显示错误消息
}
};
// 负责获取单个宝可梦详情的函数
const mapPokemon = async (name: string): Promise<PokemonType> => {
const res = await axiosInstance.get(`pokemon/${name}`);
return res.data; // 直接返回获取到的数据
};
console.log(pokedex);
return (
<>
<h1>Pokédex App</h1>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-10 m-10">
{/* 确保 pokedex 数组中有数据后再进行渲染 */}
{pokedex.map((mon: PokemonType) => (
<Pokemon key={mon.id} name={mon.name} id={mon.id} types={mon.types} />
))}
</div>
</>
);
}
export default App;让我们详细分解优化后的代码,理解其工作原理和背后的最佳实践。
collectPokemon 函数:
mapPokemon 函数:
注意事项与最佳实践:
通过上述优化,我们不仅解决了宝可梦列表乱序渲染的问题,还提升了代码的可读性、可维护性,并遵循了React中处理异步数据流的最佳实践。
正确处理React应用中的异步数据请求对于构建稳定、高性能的用户界面至关重要。本文通过一个具体的案例,深入探讨了在并发数据获取场景下,因不当的异步状态更新策略导致UI元素乱序渲染的问题。我们强调了Promise.all在协调多个并行Promise中的关键作用,以及async/await在简化异步代码方面的优势。核心思想在于:将所有异步数据获取任务聚合起来,等待它们全部完成后,再进行一次性、有序的状态更新。掌握这些模式,将帮助开发者构建更健壮、更可预测的React应用程序。
以上就是React应用中处理并发数据请求:避免状态乱序与优化渲染性能的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号