web-dev-qa-db-ja.com

レンダリングメソッドの後にcomponentWillMountの非同期呼び出しが終了する

ComponentWillMountメソッドでAPIの非同期呼び出しを実行しようとしています。実際、renderメソッドでコンポーネントにpropsを渡す必要があるため、renderメソッドをcomponentWillMountメソッドの後に実行したいと思います。

ここに私のコードがあります:

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

私のレンダリングメソッドが非同期呼び出しが完了するのを待たず、未定義の小道具を子コンポーネントに渡すように思われない理由を理解できません...

私は正しいですか?それを修正するにはどうすればよいですか?これを処理する方法は何ですか?

33

JavaScriptの非同期動作をよりよく理解する必要があるかもしれません。非同期とは「待つな」という意味です。タスクがバックグラウンドで発生し、他のコードが引き続き実行されること。これを管理する良い方法は、コンポーネントの状態を設定することです。たとえば、componentDidMountと入力する場合、loading状態をtrueに設定します。次に、非同期関数が完了したら、その状態をfalseに設定します。 render関数では、「loading ...」メッセージまたはデータを表示できます。

以下は、非同期データをフェッチする簡単な例を示すコードと、Reactでそれを処理する方法です。ブラウザで開発者ツールを開き、コンソール出力を見て、Reactライフサイクルをよりよく理解してください。

編集:2018年4月現在、新しいReactライフサイクルの推奨事項を使用するようにコードが更新されました。要約すると、componentWillMountをより安全なcomponentDidMountに置き換えました。

'componentDIDmount'が正しく示唆するように、状態を更新するのは非効率的かもしれませんafterコンポーネントはすでにマウントされています。ただし、 componentDidMountの公式Reactドキュメント に従って:

「リモートエンドポイントからデータをロードする必要がある場合、これはネットワーク要求をインスタンス化するのに適した場所です。」

「このメソッドでsetState()を呼び出すと、余分なレンダリングがトリガーされますが、ブラウザーが画面を更新する前に発生します。これにより、この場合render()が2回呼び出されても、ユーザーはt中間状態が表示されます。」

完全なサンプルコードを次に示します。

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メンテナーDan Abramovによって作成 は、何がいつ起こるかを視覚化するのに役立つと思います。

enter image description here

React 16.4の時点で、このライフサイクル図にはわずかな不正確さがあります:getDerivedStateFromPropssetStateと同様にforceUpdateの後にも呼び出されることに注意してください。 getDerivedStateFromPropsのバグ修正 に関する公式Reactブログのこの記事を参照してください。

この Reactライフサイクルダイアグラムのインタラクティブバージョン Wojciech Majによって作成されたReactバージョン> 16.04を最新の動作で選択できます(React 2019年3月27日16.8.6)。 「あまり一般的でないライフサイクルを表示する」オプションを必ずチェックしてください。

62
Todd Chaffee