なぜ 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() すると再度レンダリングが走ってしまいます。

特に今回のタスクでは

表面のログインフォーム スクリーンショット 2020-05-10 12.18.25.png

裏面の新規登録 スクリーンショット 2020-05-10 12.19.30.png

state によってフォームの内容が変わる物ですから、先に default のログインフォームがレンダリングされた後に切り替えるための state を更新してフォームを再度描写するのは無意味だということです。

今回の場合は constructor() で値を代入する

再度ドキュメントからの引用 external_link

通常、React では、コンストラクタは 2 つの目的にのみ使用されます。

  • this.state にオブジェクトを代入して ローカル state を初期化すること
  • イベントハンドラ をインスタンスにバインドすること

render 前に実行される constructor で値を設定してしまいます。
こうすることで 2回 レンダリングが走らなくなります。

実験

簡単にチェックするために render 直後に console.log() を仕込んでみました

render() { console.log('レンダリングした!'); return( ~中略~ ) }

結果

  1. componentDitMount() 内で setState() した場合
    スクリーンショット 2020-05-10 12.34.58.png
  2. constructor() 内で setState() した場合
    スクリーンショット 2020-05-10 12.36.39.png

見事レンダリングの回数を減らせています。

もう 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