Подтвердить что ты не робот

Асинхронный вызов в компонентеWillMount завершает работу после метода render

Я пытаюсь выполнить асинхронный вызов API в методе componentWillMount. В самом деле, мне бы хотелось, чтобы метод 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} />;
  }
}

Я не понимаю, почему мой метод рендеринга, похоже, не дождался завершения асинхронного вызова и передал реквизиты undefined моему дочернему компоненту...

Я прав? И что мне делать, чтобы это исправить? Каков способ справиться с этим?

4b9b3361

Ответ 1

Возможно, вам придется лучше понимать асинхронное поведение javascript. Async означает "не ждать". То, что задача будет происходить в фоновом режиме и другой код будет продолжать выполняться. Хороший способ справиться с этим - установить состояние вашего компонента. Например, когда вы вводите componentDidMount установите для состояния loading значение true. Затем, когда ваша асинхронная функция завершится, установите для этого состояния значение false. В вашей функции render вы можете либо отобразить сообщение "loading...", либо данные.

Вот некоторый код, который показывает упрощенный пример выборки асинхронных данных и того, как вы могли бы справиться с этим в React. Откройте инструменты разработчика в своем браузере и посмотрите на вывод консоли, чтобы лучше понять жизненный цикл React.

РЕДАКТИРОВАТЬ: Код был обновлен для использования новых рекомендаций жизненного цикла React по состоянию на апрель 2018. Таким образом, я заменил componentWillMount на более безопасный componentDidMount.

Может показаться неэффективным обновлять состояние после того, как компонент уже смонтирован, как правильно подразумевает "монтирование DID компонента". Однако, согласно официальной документации React для componentDidMount:

"Если вам нужно загрузить данные из удаленной конечной точки, это хорошее место для создания сетевого запроса".

"Вызов 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]
);

И вот рабочий пример на CodePen.

Наконец, я думаю, что этот образ современного жизненного цикла React, созданный сопровождающим React Даном Абрамовым, полезен для визуализации того, что происходит и когда.

enter image description here

ВНИМАНИЕ, что по состоянию на React 16.4, этот жизненный цикл схема имеет небольшую неточность: getDerivedStateFromProps теперь также называется после setState, а также forceUpdate. См. Эту статью в официальном блоге React об исправлении для getDerivedStateFromProps

Эта интерактивная версия диаграммы жизненного цикла React, созданной Wojciech Maj, позволяет вам выбрать версию React> 16.04 с последним поведением (все еще с точностью до React 16.8.6, 27 марта 2019 г.). Обязательно установите флажок "Показывать менее распространенные жизненные циклы".

Ответ 2

Приведенный выше ответ, вероятно, является чрезмерным для того, что вы пытаетесь сделать. Все, что вам нужно сделать, это сделать compoentDidMount асинхронной функцией. Затем вы можете использовать ключевое слово await при вызове функции, которая возвращает обещание.

class MyComponent extends React.Component {

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

}