React中useState在await方法内无法更新的问题求助
问题:React状态更新不生效,多次请求后state仍为空
我尝试通过多次GET请求获取宝可梦数据数组,将接口返回的response.data存入pokemonTeam状态变量,但始终无法生效。每次控制台打印pokemonTeam都显示为空数组,不过接口能正常返回正确数据。
原代码如下:
const [pokemonTeam, setPokemonTeam] = useState([]); const generatePokemon = async () => { setPokemonTeam([]); const pokemonIDs = []; do { const randomID = Math.floor(Math.random() * 121); if (!pokemonIDs.includes(randomID)) { pokemonIDs.push(randomID); } } while (pokemonIDs.length < 6); //max 6 pokemon only let promises = []; for (var i = 0; i < pokemonIDs.length; i++) { promises.push( await axios .get(`https://pokeapi.co/api/v2/pokemon/${pokemonIDs[i]}/`) .then((res) => { setPokemonTeam([...pokemonTeam, res.data]); console.log(res.data); console.log(pokemonTeam); }) .catch((err) => { console.log(err); }) ); } Promise.all(promises); console.log(pokemonTeam); };
问题原因分析
- 闭包导致state值过时:在
then回调里的pokemonTeam是函数创建时的闭包值,始终为初始空数组。所以每次执行setPokemonTeam([...pokemonTeam, res.data]),都是把当前请求的数据加到空数组里,最终state只会保留最后一条请求的数据;再加上React状态更新是异步的,打印时更新还未完成,所以控制台显示为空。 - 错误使用
await和Promise.all:循环内用await会让请求串行执行,且promises数组中存入的是请求的返回值(而非Promise对象),Promise.all(promises)没有加await,代码会直接执行最后的console.log(pokemonTeam),此时state还未完成更新。
修正方案
方案1:并行请求后一次性更新(推荐,性能更优)
先并行发起所有请求,等全部完成后一次性更新state,避免闭包和异步问题:
const [pokemonTeam, setPokemonTeam] = useState([]); const generatePokemon = async () => { setPokemonTeam([]); const pokemonIDs = []; // 生成6个不重复的随机ID do { const randomID = Math.floor(Math.random() * 121); if (!pokemonIDs.includes(randomID)) { pokemonIDs.push(randomID); } } while (pokemonIDs.length < 6); try { // 并行创建所有请求Promise const requestPromises = pokemonIDs.map(id => axios.get(`https://pokeapi.co/api/v2/pokemon/${id}/`) ); // 等待所有请求完成 const responses = await Promise.all(requestPromises); // 提取所有数据并更新state const pokemonData = responses.map(res => res.data); setPokemonTeam(pokemonData); console.log(pokemonData); // 此处可查看完整数据 } catch (err) { console.error('请求失败:', err); } };
方案2:函数式更新处理闭包(适用于需串行请求场景)
如果必须串行请求,使用函数式更新获取最新state,避免闭包陷阱:
const [pokemonTeam, setPokemonTeam] = useState([]); const generatePokemon = async () => { setPokemonTeam([]); const pokemonIDs = []; do { const randomID = Math.floor(Math.random() * 121); if (!pokemonIDs.includes(randomID)) { pokemonIDs.push(randomID); } } while (pokemonIDs.length < 6); try { for (const id of pokemonIDs) { const res = await axios.get(`https://pokeapi.co/api/v2/pokemon/${id}/`); // 用函数式更新,确保拿到最新的state setPokemonTeam(prevTeam => [...prevTeam, res.data]); } } catch (err) { console.error('请求失败:', err); } };
内容的提问来源于stack exchange,提问作者Jonathan Gonzalez
相关产品推荐
相关产品推荐

