在 React 项目中,我有一个包含一系列游戏的状态 gameResults
,我有一个函数可以根据查询获取游戏列表:
useEffect(() => {
const timeoutId = setTimeout(() => {
if (gameQuery.length > 0) {
axios.get(`/api/games/${gameQuery}`).then((response) => {
const igdbGames: IGDBGame[] = response.data.games;
const formatedGames = formatGames(igdbGames);
setGameResults(formatedGames);
});
}
}, 300);
return () => clearTimeout(timeoutId);
}, [gameQuery]);
对于每场比赛,我没有封面,所以我得到了每场比赛的封面:
const loadGamesImages = async () => {
for (let i = 0; i < gameResults.length; i++) {
axios
.get(`/api/cover/${gameResults[i].id}`)
.then((response) => {
const coverUrl: IGDBCover = response.data.covers[0];
const newGame = {
...gameResults[i],
cover: coverUrl.url.replace("//", "https://"),
};
const newGames = gameResults.filter(
(game: Game) => game.id !== newGame.id
);
setGameResults([...newGames, newGame]);
})
.catch((error) => {
console.log("error", error);
});
await sleep(300);
}
console.log("finish");
};
useEffect(() => {
loadGamesImages();
}, [gameResults.length]);
这是我的问题:当 React 更新状态时,旧状态不再存在。我解释说:对于第一个封面,新状态已经涵盖了第一场比赛。但是当他为第二场比赛创建一个新状态时,正如你所看到的,我得到了 gameResults
状态,但在这一场比赛中,第一场比赛不再有掩护。
我做错了什么?
答案 0 :(得分:1)
您的每个循环异步调用都会在有状态 gameResults
的初始绑定上关闭 - 而 gameResults
开始时为空。例如,对于第一个解析的 Promise,这些行:
const newGames = gameResults.filter(
(game: Game) => game.id !== newGame.id
);
setGameResults([...newGames, newGame]);
让 gameResults
指代空数组,所以 setGameResults
正确分布空数组加上刚刚添加的 newGame
。
但是在进一步的 Promise 解决方案中,它们还关闭了最初为空的 gameResults
- 所有异步调用都发生在组件重新渲染之前。
改用回调,这样异步调用就不会相互覆盖:
setGameResults((gameResults) => {
const newGames = gameResults.filter(
(game) => game.id !== newGame.id
);
return [...newGames, newGame];
});
(还要注意,没有必要明确指出 TS 已经可以自动推断出的参数类型:(game: Game)
可以只是 game
)
一旦它起作用了,我还建议调整您的代码,以便当效果钩子再次运行时,只有尚未被检索的封面会再次被请求。这将使您免于不必要地重复请求。