reactjs

React コンポーネント作成時に非同期通信したいときは componentDidMount に書こう

componentWillMount vs componentDidMount

「React コンポーネントを画面に表示する際に、サーバーからデータを非同期で取得して、その結果を表示に使う」というのはよくあることかと思いますが、その非同期通信処理はどこに書けばよいでしょうか?パッと浮かぶのは次のどちらかでしょう:

  • componentWillMount()
  • componentDidMount()

どちらに書くべきなのか調べてみました。

結論: componentDidMount の方がよい

表題の通りですが、componentDidMount() の方がよいみたいです。理由は下記。

プログラマーに誤解を与えない

componentWillMount() 内に setState() を書くと、なんとなく render() までに state が設定完了するように錯覚してしまいがちです。ですが、一般に非同期通信は render() 実行までに完了するとは限りません。例えば下記のコードは、データフェッチに時間がかかる場合、 render() 実行時に this.state.items を参照できずエラーになってしまいます(コードは https://daveceddia.com/watch-out-for-undefined-state/ より引用)。

class Quiz extends Component {
  componentWillMount() {
    axios.get('/thedata').then(res => {
      this.setState({items: res.data});
    });
  }

  render() {
    return (
      <ul>
        {this.state.items.map(item =>
          <li key={item.id}>{item.name}</li>
        )}
      </ul>
    );
  }
}

正しく動作させるには、下記のようなコンストラクタを書いて、初期値を設定する必要があります。

  constructor(props) {
    super(props);
    this.state = {
      items: []
    };
  }

「Will」の代わりに「Did」を使うことで、書き手は事前にコンストラクタで state の初期化が必要なことを意識しやすいですし、読み手も「通信が完了するまでに初期値で一瞬描画されるんだな」ということが理解しやすくなります。

componentWillMount は2回実行されることがある

いわゆる isomorphic な設計の場合、componentWillMount() はサーバーサイドとクライアントサイドで計2回実行されてしまうそうです。これに関しては私が isomorphic について無知なため詳しく説明できません。すみません・・・。

所感

A と B の2つの機能のうちどちらを使うか悩んだとき、「A だと X が実現できるが B ではできないから」といった論理的な理由をつい求めがちですが、「A の方が人にやさしいから」という理由でもいいんだよなあ、という気づきを得ました。

参考