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

More than 1 year has passed since last update.


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 の方が人にやさしいから」という理由でもいいんだよなあ、という気づきを得ました。


参考