なぜ setState() を ComponentDidMount() で使用してはいけないのか
些細な差だが、この処理はコンストラクタでやったほうがいい https://ja.reactjs.org/docs/react-component.html#componentdidmount external_link
レビューのリンク(GitHubに飛びます) external_link
公式ドキュメント external_link
componentDidMount() の中で、あなたはすぐに setState() を呼び出すことができます。それは余分なレンダーを引き起こしますが、ブラウザが画面を更新する前に起こります。これにより、この場合 render() が 2 回呼び出されても、ユーザには中間状態が表示されません。このパターンはパフォーマンス上の問題を引き起こすことが多いので、慎重に使用してください。ほとんどの場合、代わりに constructor() で初期状態を state に代入できるはずです。ただし、モーダルやツールチップのような場合に、サイズや位置に応じて何かをレンダーする前に DOM ノードを測定することが必要になる場合があります。
今までなんとなく使用していた setState() 公式ドキュメントに書いてあるように、 componentDidMount() 内での使用は避けた方が良さそうです。
そもそも、componentDidMount() は Component が レンダリングされた後に実行されるメソッドであって、そのタイミングで setState() すると再度レンダリングが走ってしまいます。
特に今回のタスクでは
表面のログインフォーム
裏面の新規登録
state によってフォームの内容が変わる物ですから、先に default のログインフォームがレンダリングされた後に切り替えるための state を更新してフォームを再度描写するのは無意味だということです。
今回の場合は constructor() で値を代入する
通常、React では、コンストラクタは 2 つの目的にのみ使用されます。
- this.state にオブジェクトを代入して ローカル state を初期化すること
- イベントハンドラ をインスタンスにバインドすること
render 前に実行される constructor で値を設定してしまいます。
こうすることで 2回 レンダリングが走らなくなります。
実験
簡単にチェックするために render 直後に console.log()
を仕込んでみました
render() { console.log('レンダリングした!'); return( ~中略~ ) }
結果
- componentDitMount() 内で setState() した場合
- constructor() 内で setState() した場合
見事レンダリングの回数を減らせています。
もう 1 つ実験
今回の例で言うとフォームの切り替えに直接影響する isRegistering と言う state を更新していました。
ではレンダリングに全く関係のない値を componentDitMount() 内で更新したらどうなるのでしょうか。
constructor(props) { super(props); this.state = { isRegistering: false, Meaningless: false, }; } componentDidMount() { this.setState({ Meaningless: true }); } render() { console.log('レンダリングした!'); return( ~ここでは Meaningless は使用しない~ ) }
結果
2回レンダリングされた
フォームの input を取得する時はアンチパターンではなさそうだ
管理画面などのフォームでは一度レンダリングが走った後に取得しています。
これは 1 回目にフォームをレンダリングし、その後に中身の value を componentDidMount() で取得するからです。
この場合描画差分である value のみがレンダリングされるため、無駄な描画にはならない、
そのため、今回のフォームごと表示を変える例とは違いアンチパターンにはならないのでしょう。
参考
https://stackoverflow.com/questions/35850118/setting-state-on-componentdidmount external_link