render() 方法在状态改变时并不总是被调用? (反应中的奇怪行为)

问题描述 投票:0回答:2

我正在学习 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>
    );

  }
javascript reactjs asynchronous fetch-api
2个回答
0
投票

您没有正确处理异步操作。

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
  });
}

0
投票

你的 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));
  }
© www.soinside.com 2019 - 2024. All rights reserved.