React 16.3で<React.StrictMode>
というコンポーネントが登場しました。大半のチェック点はうなずけるものだったのですが、1つ気になることがありました。
<React.StrictMode>
とは
JavaScriptをやっている方なら"use strict";
によるJavaScript自体のStrictモードはご存知かと思いますが、<React.StrictMode>
もそれと同様、今までの書き方で問題が出る部分をあぶり出すためのモードです。なお、React本体のproduction
ビルドではチェックが無効となりますので、development
ビルドを使う必要があります。
背景
React 17からレンダリングを非同期化するという話が進んでいて、それで問題になるコードをチェックするために<React.StrictMode>
が導入された、ということのようです。
deprecatedな機能のチェック
旧式になった機能を使っていると、その旨がコンソールに出力されます。
- 古い\ライフサイクル(
componentWillMount
、componentWillUpdate
、componentWillReceiveProps
) - 文字列による
ref
指定 - 旧形式のコンテキスト
findDOMNode
とはいえ、このあたりはすでにdeprecatedの宣言もあり移行手順も公開されているので、(依存しているコンポーネントが変更できないなどの状況なら別として)そう大問題にはならないかと思います。
非同期レンダリングのフェーズ
では、ここからが本題です。以前に別な記事で書きましたが、Reactの非同期レンダリングにおいては、状態が「Render Phase」と「Commit Phase」の2つに大きく分けられます。
そして、getSnapshotBeforeUpdate
、componentDidMount
、componentDidUpdate
、componentWillUnmount
はCommit Phaseのコールバックですが、それ以外のrender
、コンストラクタ、setState
のupdater、getDerivedStateFromProps
、shouldComponentUpdate
などはRender Phaseのコールバックです。
そして、非同期レンダリングを行う上では、Commit Phaseのコールバックは実行されるべき1回のレンダリングに対応して確実に1回実行されますが、Render Phaseのコールバックは複数回実行されうるものとなります。そこで、<React.StrictMode>
ではそういった問題点の洗い出しのため、render
、コンストラクタ、setState
のupdater、getDerivedStateFromProps
を1回のrender
に対して2回実行するようになります。
複数回のコンストラクタ…?
通常、updaterやrender
で副作用のあることを行おうという人はいないかと思いますが1、コンストラクタは他にも影響することを行うので、気になる例もあるかと思います。
実際にCodePenで動かして調べてみると、以下のような感じでした。
- 確かにコンストラクタは2回実行される
- もう1個生成されたはずのインスタンスは、
componentWillUnmount
も何も起こさず消滅する(あとからsetState
しようとするとエラーに)
ということで、コンストラクタでやることは「インスタンス変数の設定」、そして「外部に対しては冪等になる仕事」ということになります。
- 外部に対して破壊的に働くコードについては、
componentDidMount
に移動させるのが適当でしょう。 - CodePenでやったような、「あとから値を
setState
する」ような処理も、componentDidMount
から動かす(そして、componentWillUnmount
した際には正しく中断できるようにする)ことが必要と考えます。
-
もっとも、自分は過去に
render()
内でsetState()
しようとしたことがあります。 ↩