componentWillMount中的异步调用在render方法之后完成

Cle*_*que 36 javascript asynchronous reactjs

我试图在componentWillMount方法中对API执行异步调用.实际上我想render在componentWillMount方法之后执行该方法,因为我需要传递props给我的render方法中的组件.

这是我的代码:

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} />;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Tod*_*fee 68

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

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

编辑:代码已更新为使用新的React生命周期建议截至2018年4月.总之,我componentWillMount更安全componentDidMount.

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

"如果你需要从远程端点加载数据,这是一个实例化网络请求的好地方."

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

这是完整的示例代码:

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]
);
Run Code Online (Sandbox Code Playgroud)

这是CodePen上工作示例.

最后,我认为React维护者Dan Abramov对现代React生命周期的这种形象有助于可视化发生的事情和时间.

在此输入图像描述

注意,从React 16.4开始,这个生命周期图有一个小的不准确性:getDerivedStateFromProps现在也被setState称为forceUpdate.请参阅官方React博客中有关getDerivedStateFromPropsBugfix的文章

React生命周期图的交互式版本允许您选择具有最新行为的React版本16.04.确保选中"显示不太常见的生命周期"选项.

  • 注意,从React 16.4开始,这个生命周期图有一个小的不准确性:`getDerivedStateFromProps`现在也在`setState`之后调用(以及`forceUpdate`?).请参阅https://reactjs.org/blog/2018/05/23/react-v-16-4.html#bugfix-for-getderivedstatefromprops (3认同)
  • @MattKramer感谢您指出这一点。我已将答案更新为包含该注释。 (2认同)