【问题标题】:render() method isn't always called when state changes? (Weird behaviour in react)状态改变时并不总是调用 render() 方法? (反应中的奇怪行为)
【发布时间】:2021-03-21 12:07:35
【问题描述】:

我正在学习 react 并尝试使用 football-data API 编写前 5 名联赛的足球排名,但出现了一个奇怪的问题。 我读到每次状态改变时,都会调用 render() 。在我的代码中,从 API 获取数据后不会调用 render()。 getAllStandings() 方法在开始时被调用,对于competitionsIds 数组中的每个competitionId,它都会获取排名。在迭代每个 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 api asynchronous fetch


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-07
        • 2019-09-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多