Javascript 渲染方法后,componentWillMount 中的异步调用完成

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43312223/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:50:04  来源:igfitidea点击:

Asynchronous call in componentWillMount finishes after render method

javascriptreactjsasynchronous

提问by Clement Levesque

I am trying to perform an asynchronous call to an API in the componentWillMount method. Indeed I would like the rendermethod to executed after the componentWillMount method as I need to pass propsto the component in my rendermethod.

我正在尝试对 componentWillMount 方法中的 API 执行异步调用。事实上,我希望该render方法在 componentWillMount 方法之后执行,因为我需要props在我的render方法中传递给组件。

Here is my code :

这是我的代码:

class TennisSearchResultsContainer extends React.Component {
  componentWillMount () {
    // TODO: Build markers for the map
    // TODO: Check courtsResults object and database for tennis court
    this.courtsMarkers = this.props.courtsResults.map((court) => {
      return new google.maps.Marker({
        position: new google.maps.LatLng(JSON.parse(court.LOC).coordinates[1], JSON.parse(court.LOC).coordinates[0]),
        title: court.NAME,
        animation: google.maps.Animation.DROP
      });
    });
  }
  render () {
    return <TennisSearchResults criterias={this.props.criterias} courtsMarkers={this.courtsMarkers} />;
  }
}

I don't understand then why my render method seems to do not wait for the asynchronous call to finish and pass undefined props to my child component...

我不明白为什么我的渲染方法似乎不等待异步调用完成并将未定义的道具传递给我的子组件......

Am I right? And what should I do to fix that? What is the way to handle this?

我对吗?我该怎么做才能解决这个问题?处理这种情况的方法是什么?

回答by Todd Chaffee

You might need to understand javascript async behavior better. Async means "don't wait". That the task will happen in the background and other code will continue to execute. A good way to manage this is to set state on your component. For example, when you enter componentDidMountset a loadingstate to true. Then when your async function completes, set that state to false. In your renderfunction you can then either display a "loading..." message or the data.

您可能需要更好地了解 javascript 异步行为。异步意味着“不要等待”。任务将在后台发生,其他代码将继续执行。管理此问题的一个好方法是在您的组件上设置状态。例如,当您输入时,componentDidMountloading状态设置为true。然后,当您的异步功能完成时,将该状态设置为false. 在您的render函数中,您可以显示“正在加载...”消息或数据。

Here is some code that shows a simplified example of fetching data async and how you could handle that in React. Open the developer tools in your browser and look at the console output to understand the React lifecycle better.

下面是一些代码,显示了异步获取数据的简化示例以及如何在 React 中处理该示例。在浏览器中打开开发人员工具并查看控制台输出以更好地了解 React 生命周期。

EDIT: Code has been updated to use the new React Lifecycle recommendations as of April 2018. In summary, I replaced componentWillMountwith the safer componentDidMount.

编辑:截至 2018 年 4 月,代码已更新为使用新的 React Lifecycle 建议。总之,我替换componentWillMount为更安全的componentDidMount.

It might seem inefficient to update the state afterthe component has already mounted, as 'componentDIDmount' correctly implies. However, per the official React documentation on componentDidMount:

正如“组件DID安装”正确暗示的那样,在组件已经安装之后更新状态似乎效率低下。但是,根据关于 componentDidMount官方 React 文档

"If you need to load data from a remote endpoint, this is a good place to instantiate the network request."

“如果您需要从远程端点加载数据,这是实例化网络请求的好地方。”

"Calling setState()in this method will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render()will be called twice in this case, the user won't see the intermediate state."

“调用setState()这个方法会触发额外的渲染,但它会在浏览器更新屏幕之前发生。这保证了即使render()在这种情况下会被调用两次,用户也不会看到中间状态。”

Here's the complete example code:

这是完整的示例代码:

class MyComponent extends React.Component {
  constructor(props) {
    super();

    console.log('This happens 1st.');

    this.state = {
      loading: 'initial',
      data: ''
    };

  }

  loadData() {
    var promise = new Promise((resolve, reject) => { 
      setTimeout(() => {
        console.log('This happens 6th (after 3 seconds).');
        resolve('This is my data.');
      }, 3000);
    });

    console.log('This happens 4th.');

    return promise;
  }

  componentDidMount() {

    console.log('This happens 3rd.');

    this.setState({ loading: 'true' });
    this.loadData()
    .then((data) => {
      console.log('This happens 7th.');
      this.setState({
        data: data,
        loading: 'false'
      });
    });
  }  

  render() {

    if (this.state.loading === 'initial') {
      console.log('This happens 2nd - after the class is constructed. You will not see this element because React is still computing changes to the DOM.');
      return <h2>Intializing...</h2>;
    }


    if (this.state.loading === 'true') {
      console.log('This happens 5th - when waiting for data.');
      return <h2>Loading...</h2>;
    }

    console.log('This happens 8th - after I get data.');
    return (
      <div>
        <p>Got some data!</p>
        <p>{this.state.data}</p>
       </div>
    );
  }
}

ReactDOM.render(
  <MyComponent />,
  document.getElementsByClassName('root')[0]
);

And here is the working example on CodePen.

这是CodePen 上工作示例

Finally, I think this image of the modern React lifecycle created by React maintainer Dan Abramovis helpful in visualizing what happens and when.

最后,我认为由 React 维护者 Dan Abramov 创建的现代 React 生命周期图像有助于可视化发生的事情和时间。

enter image description here

在此处输入图片说明

NOTE that as of of React 16.4, this lifecycle diagram has a small inaccuracy: getDerivedStateFromPropsis now also called after setStateas well as forceUpdate. See this article from the official React blog about the Bugfix for getDerivedStateFromProps

请注意,从 React 16.4 开始,这个生命周期图有一个小的不准确之处:getDerivedStateFromProps现在也调用 aftersetStateforceUpdate. 请参阅 React 官方博客中有关getDerivedStateFromProps 的错误修复的这篇文章

This interactive version of the React lifecycle diagramcreated by Wojciech Maj allows you to select React version >16.04 with the latest behavior (still accurate as of React 16.8.6, March 27, 2019). Make sure you check the "Show less common lifecycles" option.

这个由 Wojciech Maj 创建的 React 生命周期图的交互式版本允许您选择具有最新行为的 React 版本 >16.04(截至 React 16.8.6,2019 年 3 月 27 日仍然准确)。确保选中“显示不太常见的生命周期”选项。

回答by Keith Blanchard

The above answer is probably overkill for what you are trying to do. All you need to do is make compoentDidMount an async function. Then you can use the await keyword on a function call that returns a promise.

上面的答案对于您尝试做的事情来说可能有点矫枉过正。您需要做的就是使 compoentDidMount 成为一个异步函数。然后您可以在返回承诺的函数调用上使用 await 关键字。

class MyComponent extends React.Component {

  async componentWillMount () {
  
    await myAsyncCall();
    
  }
  
  render () {
    
  }

}