首页 > 解决方案 > 尽管处于状态,但在 forEach 函数中进行 Axios 调用以修改对象在渲染中未被识别

问题描述

我正在尝试学习完整堆栈 React 应用程序的基础知识,但我的 API 调用遇到了一个非常奇怪的问题。

我想从作为对象数组返回的数据库中检索预告片列表。然后,我使用 promise 循环该数组,并对数组中的每个项目进行单独的 API 调用,以向对象添加键值对。在我构建了该对象数组后,我将其设置为我的应用程序状态,然后通过道具将其传递给子组件,该子组件应根据数组中的信息呈现元素列表。

现在,控制台记录组件接收的数组和所有数据,但是,除了在 forEach 循环中添加的字段之外,每个字段都可以呈现。

我已经从 forEach 循环中删除了 API 调用,而只是在循环中设置了一个静态值,并且信息会按预期正确呈现。所以我确定问题出在循环中的 API 调用上。我不知道为什么。日志显示数组是完整的,所以渲染器找不到数据似乎没有意义。

// The API call

loadUserAndTrailers = () => {
        let trailers = []; 
        axios.get("http://localhost:5000/api/trailers")
        .then (res => {
            trailers = res.data;
        })
        .then (() => {          
            trailers.forEach(trailer => {

 axios.get(`http://localhost:5000/api/votes/user/${this.state.user.id}/${trailer.TrailerID}`)
                .then (res => {
                    const vote = res.data[0].Vote;
                    trailer.vote = vote;
                })
            })
        })
        .then (() => {
            this.setState({trailers: trailers});
        })
}

// The child component

const TrailerList = props => {

    console.log(props)

    const trailers = props.trailers.map(trailer => {
        return <div>{trailer.vote}</div>
    });

    return <div>{trailers}</div>;
};

// The return in the parent component

return (
  <div className="container">
    <div className="content">
      <h1>Trailers</h1>
      <div className="trailers">
      <TrailerList trailers={this.state.trailers}></TrailerList>
    </div>
  </div>
</div>
);

console.log(props) 向我显示了一个完整的数组,其中包含一个对象数组,该键值存在 { vote: 1 } 但它什么也不渲染。怎么会这样?我已经把头撞到墙上两天了,但我承认我对 API 和 Promise 很陌生。我的印象是,承诺应该确保在继续下一步之前完成调用,并且我正确记录的状态似乎暗示代码的该方面按预期运行。

标签: reactjsapiaxios

解决方案


console.log(props) 向我显示了一个完整的数组,其中包含一个对象数组,该键值存在 { vote: 1 } 但它什么也不渲染。怎么会这样?

定时。您启动循环,拨打电话,然后不要等到他们完成;你setState会遇到未填充的预告片,然后一段时间后 axios 会开始填充它们。

需要了解的一件事console.log(至少在 Chrome 中)是它在渲染对象时也是异步的。您记录的任何字符串都将按原样记录;但是对象需要时间来绘制,当浏览器开始使用它时,它可能会在绘制时绘制对象而不是在记录时。所以你看到的不是setState看到的。请参阅Chrome 的 JavaScript 控制台是否懒于评估数组?使用JSON.stringify或挑选原始值来记录真实存在的内容。

您需要做的是确保then循环之后出现在循环之后(即当所有结果都完成时)。要做到这一点,有两件事需要发生。

1)现在,axios循环的每次迭代(及其then链)中的承诺都被丢弃了——没有任何东西在等待它。如果我们返回它,并把它forEach变成 a map,我们会得到一个 promise 数组。

2) 要等待数组中的所有承诺,请使用Promise.all.

所以...

.then (() =>
  Promise.all(trailers.map(trailer =>
    axios.get(`${baseUrl}/${this.state.user.id}/${trailer.TrailerID}`)
    .then(res => {
      const vote = res.data[0].Vote;
      trailer.vote = vote;
    })
  ))
})

推荐阅读