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