React v17の破壊的変更について改めて確認
v17.0.0のリリースはもう2年近く前のことだし、v18もリリースされてv17はもう最新メジャーバージョンでもないので今更感が強いけども、改めてv17での破壊的変更を確認する。
以下のReact公式の記事で破壊的変更について確認しつつ、必要に応じてStackBlitzで挙動をv16とv17で比較する。
なお、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イベントはバブリングしない、ネイティブのそれと同じ挙動へ変更された - 子要素でのスクロール時に親要素でスクロールイベントが発生しない
onFocusをfocusinに、onBlurをfocusoutにそれぞれ変更
このイベントと
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ではfocusin、focusoutをサポートしていないので、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.
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.
イベントプーリングの廃止
v16
-
SyntheticEventは再利用され、イベント実行後に都度プロパティをnullにするので、イベント実行後にアクセスしようとするとエラーになる - イベント実行後もプロパティを利用可能にするには
e.persist()を呼ぶ(もしくは変数に持たせておく)必要がある
v17
- イベントプーリング自体がなくなった
-
e.persist()は実行できるけども、これを実行しても何もしない
useEffectのクリーンアップタイミングを非同期に
v16
- 副作用のクリーンアップは同期的に実行されていた
v17
- 副作用のクリーンアップは常に非同期的に実行
- 副作用のコールバック関数では参照できた値がクリーンアップ関数実行時に
nullとなり得るが、その値を変数に保持しておくことで回避できる - ESLintのルールが入っていれば防止できているはず
コンポーネントでundefinedを返すと一貫してエラーになるよう変更
v16
-
memoやforwardRefに限りコンポーネントがundefinedをreturnできた
v17
-
memoやforwardRef含めて全てのコンポーネントがundefinedをreturnできなくなった - 何も返したくない場合は
undefinedではなくnullを使う
コンポーネントスタックトレースの改善
エラー時に本番環境でもコンポーネント名そのままでエラーが起きた経路を追跡できるコンポーネントスタックトレースになったという理解。
これを実現するための方法が破壊的な形で実現されていることで、破壊的な変更として扱っているらしい。
プライベートなものをエクスポートから削除
React Native for Webのみに関係するはず。
加えてReactTestUtils.SimulateNativeも削除となっている。
これらを使っていない限りあまり気にする必要がなさそう。
Discussion