React v16.9の変更点

少し前に約3ヶ月ぶりのマイナーバージョンアップが来ていたので、変更点を確認。公式に沿っているので、特に目新しい情報が記載されているわけではありません。あしからず。

非推奨項目

アンカータグの”javascript:”

アンカータグにjavascript:を使う記述が非推奨になりました。

// Warning!
const App = () => <a href="javascript: alert('hello')">click</a>

使用はできますが、v16.9ではコンソールに警告が表示されます。dangerouslySetInnerHTMLを使えば警告は表示されませんが、あえて使用するケースも少ないでしょう。

Factory Component

renderメソッドでオブジェクトを返すfactoryコンポーネントが非推奨になりました。

function FactoryComponent() {
    return { render() { return <div />; } };
}

動的にクラス名を作成する場合など、コンポーネントを作成する処理を記述する用途で利用されていたようです。

ライフサイクルメソッド

以下のライフサイクルメソッドがリネームされました。

  • componentWillMount→UNSAFE_componentWillMount
  • componentWillReceiveProps→UNSAFE_componentWillReceiveProps
  • componentWillUpdate→UNSAFE_componentWillUpdate

リネームされたメソッドは、16.9と今後リリースされる17.xでも機能します。今あるコードを自動でリファクタリングしたい場合には、公式で示されている以下のコマンドを実行します。

npx react-codemod rename-unsafe-lifecycles

Async act()

過去記事で取り上げたように、React Hooksのテストを行う際にact()が導入されました。このユーティリティメソッドが非同期処理に対応するようになりました。

await act(async() => {
});

機能の追加

React.Profiler

React v16.5で追加されたボトルネックを調査するReact DevToolsの大規模アプリケーション版だそうです。要はレンダリング時のコストを把握するAPIですね。

メモ化など、レンダリング時のボトルネックを改善するために利用します。

使用方法

Profilerをインポートし、ボトルネックを調査したい場所にラップします。コンポーネントにはidonRenderを定義します。後者は再レンダリングが発生するたびにコールされるコールバック関数で、レンダリングにかかった時間などの情報を受け取ります。

<Profiler id="Check" onRender={onRenderCallback}>
    <Component />
</Profiler>

onRenderコールバック関数は以下の引数を受け取ります。

function onRenderCallback(
  id,
  phase,
  actualDuration,
  baseDuration,
  startTime,
  commitTime,
  interactions
) {
  // ログの書き出しなど
}
  • id – Profilerで指定したid
  • phase – フェーズ状態。”mount”か”update”が格納(下記参照)
  • actualDuration – 更新のレンダリングに要した時間。通常、メモ化すると必要部分のみを更新するため、マウント後以降はこの値が減少していくのが理想的
  • baseDuration – メモ化せずにツリー全体をレンダリングする場合の予想時間。actualDurationとの対比に使われると思われます。
  • startTime – 現在の更新レンダリングを開始したときのタイムスタンプ
  • commitTime – Reactが現在の更新をコミットしたときのタイムスタンプ
  • interactions – Interaction tracingのセット。指定しないと空のSet配列が返る

Reactはrenderとcommitという2つのフェーズを持っています。renderフェーズは変更を行う必要があるかを決定し、その結果を以前の内容と比較します。commitフェーズは、実際にDOMへの挿入や削除などの変更になります。

出力例

以前解説したReact.memoのサンプルをProfilerでラップし、連続レンダリングしたものを単に出力してみると、例えば以下のような値が出力されます。

コンソール結果

actualDurationとbaseDurationに注目します。100回再レンダリングし、メモ化前後で比較してみました(抜粋)。実際に再レンダリングされるコンポーネントは2の累乗あります。

非メモ化とメモ化の比較

メモ化しない場合は更新のたび追加以外のコンポーネントも更新されるため、次第にactualDurationが増えていきます。

尚、Profilerはコストがかかるため、プロダクション時は自動で無効になるそうです。