我正在学习 React 并尝试使用足球数据 API 编写前 5 名联赛的足球排名,但有一个奇怪的问题。
我读到每次状态改变时,都会调用
render()
。在我的代码中,从 API 获取数据后不会调用 render()
。
方法
getAllStandings()
在开始时被调用,对于 competitionId
数组的每个 competitionsIds
,它都会获取站立值。迭代每个 id 后,状态会更新,并且应该调用 render()
方法,但事实并非如此。 render()
方法中的变量为空。
也许是由于异步功能?
这是我的代码(
getAllStandings()
在componentDidMount()
方法中调用):
getAllStandings(){
let list = [];
let competitions = this.state.competitionsIds; // [1,2,3,4,5]
console.log("Competitions: ", competitions);
competitions.forEach((item, i) => {
this.getStandingById(item).then((data) => list.push(data));
});
this.setState({
standingsList: list
},() => console.log("standingsList: ", this.state.standingsList));
}
async getStandingById(competitionId) {
//console.log("Fetching standing of competition with id: ", competitionId);
const url = this.state.url.concat("competitions/").concat(competitionId).concat("/standings/");
const options = {
method: 'GET',
headers: {
'X-Auth-Token': this.state.auth
}
};
let response = await fetch(url, options);
let data = await response.json();
return await data;
}
render() {
let standingsArr = this.state.standingsList;
console.log("standingsArr: ", JSON.stringify(standingsArr));
console.log("length: ", standingsArr.length);
return (
<div className="App">
<div>{this.state.standingsList.map((item) => (
<TableComp name="yes"/>
))}</div>
</div>
);
}
您没有正确处理异步操作。
let list = [];
console.log("Competitions: ", competitions);
competitions.forEach((item, i) => {
this.getStandingById(item).then((data) => list.push(data));
});
this.setState({
standingsList: list
},() => console.log("standingsList: ", this.state.standingsList));
forEach
将在对 getStandingById
的所有调用完成之前完成,因此您的 list
将不会拥有您想要的所有数据。您需要等待所有这些调用完成才能调用 setState
。将它们全部收集在一个数组中并使用 Promise.all 可以做到这一点:
let list = [];
console.log("Competitions: ", competitions);
const promises = [];
competitions.forEach((item) => {
const promise = this.getStandingById(item).then((data) => list.push(data));
promises.push(promise);
});
Promise.all(promises).then(() => {
this.setState({
standingsList: list
});
});
注意,数据推送到
list
的顺序每次都不会相同。如果顺序很重要,则不要使用 forEach
,因为它们都会并行启动。相反,您可以使用正常的 for
循环和 await
每次调用 getStandingById
,然后再继续下一场比赛,如下所示:
async getAllStandings() {
let list = [];
console.log("Competitions: ", competitions);
for (const item of competitions) {
// do them sequentially to preserve order
await this.getStandingById(item).then((data) => list.push(data));
});
this.setState({
standingsList: list
});
}
你的 getStandingById 函数有问题,你的 forEach 将在调用 setState 之前结束,所以你需要等待你的承诺的结果,你可以使用包装在 Promise.all 中的地图来做到这一点,注意我改变了getAllStandings 到异步函数。
async getAllStandings(){
let competitions = this.state.competitionsIds; // [1,2,3,4,5]
console.log("Competitions: ", competitions);
const list = await Promise.all(competitions.map((item, i) => {
return this.getStandingById(item);
}));
this.setState({
standingsList: list
},() => console.log("standingsList: ", this.state.standingsList));
}