🦁

React v17の破壊的変更について改めて確認

2022/06/22に公開

v17.0.0のリリースはもう2年近く前のことだし、v18もリリースされてv17はもう最新メジャーバージョンでもないので今更感が強いけども、改めてv17での破壊的変更を確認する。
以下のReact公式の記事で破壊的変更について確認しつつ、必要に応じてStackBlitzで挙動をv16とv17で比較する。

https://ja.reactjs.org/blog/2020/10/20/react-v17.html
https://ja.reactjs.org/blog/2020/08/10/react-v17-rc.html#other-breaking-changes

なお、StackBlitzでの検証のものはそれぞれ以下のバージョンをdependenciesとして指定している。

react, react-dom dependencies
v16 16.14.0
v17 17.0.2

イベントデリゲーションの対象がReactアプリケーションのルート要素に

v16

  • Reactアプリケーション内部で登録したイベントはdocumentに委譲される
  • Reactアプリケーションのルートの要素よりも親の要素でイベント登録している場合に、Reactアプリケーション内部で登録しているイベントが阻害され得る

v17

  • Reactアプリケーション内部で登録したイベントはReactアプリケーションのルート要素に委譲される
  • 委譲先がReactアプリケーションのルートなので、Reactアプリケーションのルートの要素よりも親の要素でイベント登録していても、Reactアプリケーション内部で登録しているイベントが阻害されることはない

scrollイベントのバブリングを廃止

v16

  • 全てのイベントがバブリングするようになっているので、scrollイベント例外ではなくバブリングする
  • 子要素内でスクロールしているときに親要素でもスクロールイベントが発生し得る
  • e.target === e.currentTargetによる比較でハンドリングすることがv16であれば回避策となりそう

v17

  • scrollイベントはバブリングしない、ネイティブのそれと同じ挙動へ変更された
  • 子要素でのスクロール時に親要素でスクロールイベントが発生しない

https://github.com/reactjs/rfcs/issues/175

onFocusfocusinに、onBlurfocusoutにそれぞれ変更

このイベントと focus との主な違いは、 focusin がバブリングを行うのに対し focus は行わないことです。
https://developer.mozilla.org/ja/docs/Web/API/Element/focusin_event

このイベントと blur との主な違いは、 focusout がバブリングするのに対し blur はしないことです。
https://developer.mozilla.org/ja/docs/Web/API/Element/focusout_event

ただ、Reactがバブリングは常にさせていたので、上記の差異が表面化することはないはず。

古いバージョン(v52以前)のFirefoxではfocusinfocusoutをサポートしていないので、React v17でFirefox v52以前に対するサポートを切ることを意味する。

In terms of breaking changes, this change will mean that React will not support Firefox versions earlier than 52. Earlier versions do not support focusin and focusout.

https://github.com/facebook/react/pull/19186

on*Captureをブラウザネイティブのキャプチャフェーズで行うように

on*Captureイベントがブラウザネイティブのキャプチャフェーズで行われるようになった。
v16ではブラウザネイティブのバブリングイベントリスナーでキャプチャフェーズとバブルフェーズを仮想化していたらしい。

This is a deparature of the previous logic, which "virtualized" the capture and bubble phases within the native bubble event listener.

https://github.com/facebook/react/pull/19221

イベントプーリングの廃止

v16

  • SyntheticEventは再利用され、イベント実行後に都度プロパティをnullにするので、イベント実行後にアクセスしようとするとエラーになる
  • イベント実行後もプロパティを利用可能にするにはe.persist()を呼ぶ(もしくは変数に持たせておく)必要がある

v17

  • イベントプーリング自体がなくなった
  • e.persist()は実行できるけども、これを実行しても何もしない

https://github.com/facebook/react/pull/18969

useEffectのクリーンアップタイミングを非同期に

v16

  • 副作用のクリーンアップは同期的に実行されていた

v17

  • 副作用のクリーンアップは常に非同期的に実行
  • 副作用のコールバック関数では参照できた値がクリーンアップ関数実行時にnullとなり得るが、その値を変数に保持しておくことで回避できる
  • ESLintのルールが入っていれば防止できているはず

https://github.com/facebook/react/pull/17925

コンポーネントでundefinedを返すと一貫してエラーになるよう変更

v16

  • memoforwardRefに限りコンポーネントがundefinedreturnできた

v17

  • memoforwardRef含めて全てのコンポーネントがundefinedreturnできなくなった
  • 何も返したくない場合はundefinedではなくnullを使う

https://github.com/facebook/react/pull/19550

コンポーネントスタックトレースの改善

エラー時に本番環境でもコンポーネント名そのままでエラーが起きた経路を追跡できるコンポーネントスタックトレースになったという理解。
これを実現するための方法が破壊的な形で実現されていることで、破壊的な変更として扱っているらしい。

https://github.com/facebook/react/pull/18561

プライベートなものをエクスポートから削除

React Native for Webのみに関係するはず。
加えてReactTestUtils.SimulateNativeも削除となっている。
これらを使っていない限りあまり気にする必要がなさそう。

GitHubで編集を提案

Discussion