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