React v17.0 Release Candidate: 新機能「なし」
日本語版サイトでは英語版ブログに定期的に追従しつつ、2020 年以降の記事を随時翻訳しています。
このブログはアーカイブされています。最新の記事は ja.react.dev/blog でご覧ください。
本日 React 17 の最初のリリース候補を公開します。前回の React メジャーリリースから 2 年半が経過しており、これは我々の基準からしても長いものです! このブログ記事では、このメジャーリリースの役割、期待される変化、そしてどのように試すことができるのかについて説明します。
新機能 “なし”
この React 17 リリースは普段のリリースと異なっており、開発者向けの新機能が何も追加されていません。代わりに、このリリースは React 自体のアップグレードを簡単にすることに焦点を当てています。
我々は React の新機能を積極的に開発中ですが、それらの新機能はこのリリースに含まれていません。React 17 のリリースは、誰も取り残されないようにしつつ新機能を展開していくという私たちの戦略の鍵となるものです。
具体的には React 17 は、あるバージョンの React で管理されるツリー内に、別のバージョンの React で管理されるツリーをより安全に埋め込めるようにするための、「踏み台」となるリリースとなっています。
段階的なアップグレード
過去 7 年間、React のアップグレードは “all-or-nothing” 型でした。古いバージョンにとどまり続けるか、新バージョンにアプリ全体をアップグレードするかの二択です。その中間は存在しませんでした。
これまではそれで何とかなっていましたが、我々は “all-or-nothing” なアップグレード戦略の限界に直面しています。例えば、古いコンテクスト API の非推奨化のようないくつかの API 変更への対応は、自動化して行うことができません。最近に書かれたアプリのほとんどはこれを一切使っていないにも関わらず、React はこれをサポートし続けています。ずっとこれをサポートし続けるか、幾つかのアプリを古いバージョンの React のまま取り残すしかしかないのです。どちらの選択肢もあまり望ましくありません。
そこで我々はもうひとつの選択肢を用意することにしました。
React 17 は段階的な React のアップグレードを可能にします。React 15 から 16 に(そして近い将来 React 16 から 17 に)アップグレードする場合、普通はアプリ全体をまとめてアップグレードします。これは多くのアプリではうまく行きます。しかしコードが数年以上前に書かれており活発にメンテされていないような場合、だんだんと難易度が増していきます。ページ内で 2 つの React のバージョンを混在させることは可能ですが、React 17 以前にこのようなことをすると不安定になり、イベント絡みの問題が引き起こされていました。
React 17 で、これらの問題の多くを修正します。これは React 18 やもっと将来のバージョンが来たときに、とれる選択肢が増えるということを意味します。選択肢のひとつは、これまでやってきたのと同様、アプリ全体を一度にアップグレードするというものです。しかし今後は、アプリを一部分ずつアップグレードするという選択肢がとれるようになります。例えば、アプリの大部分を React 18 に移行しつつ、いくつかの遅延ロードされるダイアログやサブページを React 17 のままにしておけるようになります。
だからといって段階的に更新しないといけないという訳ではありません。今後もほとんどのアプリでは、一気にアップグレードするのがベストの選択肢です。2 つのバージョンの React をロードするというのは、たとえ片方はオンデマンドで遅延ロードするのだとしても、やはり理想的ではありません。しかし、活発にメンテされていない大きなアプリではこの選択肢は検討に値するものであり、React 17 はこのようなアプリが取り残されずに済むようにします。
段階的なアップグレードを可能にするために、React のイベントシステムに幾つかの変更を加える必要がありました。React 17 がメジャーリリースとなっているのは、これらの変更が一部互換性の問題を引き起こす可能性があるからです。実際のところは、10 万を超えるコンポーネントの中で変更する必要があったのは 20 未満でしたので、ほとんどのアプリは React 17 にトラブルなく移行できると考えています。問題があった場合は知らせてください。
段階的アップグレードのデモ
古いバージョンの React を必要に応じて遅延ロードするという手法をデモするためのサンプルリポジトリを用意しました。このデモは Create React App を使っていますが、他のどのようなツールでも似たアプローチが可能なはずです。他のツールを使ったデモを追加するプルリクエストを歓迎します。
補足
その他の変更は React 17 より後に延期しました。このリリースの目標は段階的なアップグレードを可能にすることです。React 17 自体へのアップグレードが難しいようでは本リリースの目的が台無しですので、そのようなことはないはずです。
イベントデリゲーションに関する変更
異なるバージョンの React で作成されたアプリをネストさせることは、技術的にはこれまでも可能でしたが、React のイベントシステムの挙動に起因して不安定なものとなっていました。
React コンポーネントでは、通常はイベントハンドラをインラインで記載します:
<button onClick={handleClick}>
このコードは素の DOM では以下と同等です:
myButton.addEventListener('click', handleClick);
しかし、ほとんどのイベントでは、実際には React はあなたが宣言した DOM ノードにイベントハンドラをアタッチするのではありません。代わりに、イベントタイプごとにハンドラを 1 つだけ、document
ノードに直接アタッチします。これはイベントデリゲーション(event delegation; イベントの委譲)と呼ばれます。大きなアプリケーションツリーではパフォーマンス面で有利であるということに加え、これによりイベントのリプレイといった新機能も追加しやすくなります。
React は最初のリリース時点からこのようなイベントデリゲーションを自動的に行っていました。ドキュメントで DOM イベントが発生すると、React はどのコンポーネントを呼び出すべきか判定し、React のイベントがあなたのコンポーネントツリー内を上側に向かって「バブリング」していきます。しかしその裏側では、この時点でネイティブのイベントが既に document
のレベルにバブリングし終わっているのであり、そこに React はイベントハンドラを仕込んでいるのです。
ところがこの挙動が、段階的なアップグレードにおいて問題を引き起こします。
ページ内に複数の React バージョンがあると、それらがすべてイベントハンドラをトップレベルに登録します。これにより e.stopPropagation()
がおかしくなります。例えばネストされている側のツリーがとあるイベントの伝播を停止した場合でも、外側のツリーがそれを受け取ることができてしまいます。これが複数の異なるバージョンの React をネストさせるのが難しかった理由です。この懸念は架空の話ではなく、例えば Atom エディタは 4 年前に実際にこの問題に遭遇しています。
以上が、React が裏で DOM にイベントをアタッチする方法を、我々が変更しようとしている理由です。
React 17 では、React は document
レベルにイベントハンドラをアタッチしないようになります。代わりに、あなたが React ツリーをレンダーしようとしているルート DOM コンテナに対してアタッチするようになります。
const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);
React 16 およびそれ以前では、React はほとんどのイベントに対して document.addEventListener()
のようにしていました。代わりに React 17 は、水面下で rootNode.addEventListener()
という呼び出しを行うようになります。
この変更のおかげで、あるバージョンの React で管理されているツリーを別バージョンの React で管理されているツリー内に埋め込むことが、より安全に行えるようになります。ただしこれがうまく働くためにはどちらの React のバージョンも 17 以上である必要があり、これが React 17 にアップグレードすることが重要である理由です。ある意味で、React 17 は、今回以降に段階的なアップグレードを行いやすくするための「踏み台」リリースであると言えます。
この変更により、他のテクノロジーで構築されたアプリ内に React を組み込むことも容易になります。例えば、外見の大部分を jQuery で書いており、その中の新しいコードが一部 React で書かれているという場合でも、今後は「React 内で e.stopPropagation()
が呼ばれたイベントは jQuery のコードに到達しない」という(期待通りの)動作をするようになります。逆のことも言えます。もし React が好きではなくなってアプリを(例えば jQuery で)書き直したくなったとしても、イベント伝播処理を壊すことなく見た目部分を React から jQuery に移行することができるようになります。
この新しい挙動により、React コードと非 React コードの統合に関して 過去 数年に わたって 報告 されて きた 様々な 問題が 解決 される ことが分かっています。
補足
この変更によりルートコンテナ外にあるポータルの動作がおかしくなるのではと思っているかもしれません。が、React はポータルのコンテナでもイベントをリッスンするようになるため、問題は生じません。
問題が出た場合の修正方法
どのような破壊的変更もそうですが、コードの一部は調整する必要があるかもしれません。Facebook では(何千ものモジュールがあるうち)合計で約 10 個のモジュールを、今回の変更に合わせて調整する必要がありました。
例えば document.addEventListener(...)
という形で手動で DOM リスナを登録すれば、それにより React のすべてのイベントが捕捉できると期待するかもしれません。React 16 以前では、React のイベントハンドラ内で e.stopPropagation()
をコールしていたとしても、document
にあるあなたのカスタムのリスナはこのイベントを受け取っていました。なぜならネイティブのイベントは、ドキュメントのレベルに既に到達していたからです。React 17 以降は、イベントの伝播は(指示された通りに!)止まるため、あなたが document
に書いたハンドラは呼ばれなくなります。
document.addEventListener('click', function() {
// This custom handler will no longer receive clicks
// from React components that called e.stopPropagation()
});
このようなコードを修正するには、イベントリスナがキャプチャフェーズで呼ばれるようにコードを変更します。そのためには document.addEventListener
の第 3 引数として { capture: true }
を渡します:
document.addEventListener('click', function() {
// Now this event handler uses the capture phase,
// so it receives *all* click events below!
}, { capture: true });
この方針は、全体的にはむしろ障害に強くなるものだということに注意してください。この変更で、例えば、e.stopPropagation()
が React イベントハンドラ外で呼ばれた場合に発生する既存のバグもおそらく修正されることでしょう。言い換えると、React 17 でのイベント伝播は普通の DOM に近くなったということです。
その他の破壊的変更
React 17 での破壊的変更は最小限にとどめてあります。例えば、前のバージョンで非推奨化されたメソッドの削除は行っていません。ただし、我々の経験で比較的安全であった破壊的変更が、少数のみ含まれています。我々のコンポーネントでこれらにより修正する必要があったコンポーネントは 10 万以上あるうちの 20 未満でした。
ブラウザとの整合性向上
イベントシステムに幾つかの小さな変更を加えました:
- よくある誤解を防ぐため、
onScroll
イベントはバブリングしないようになりました。 - React の
onFocus
とonBlur
イベントはネイティブのfocusin
とfocusout
イベントを裏で使うように変更されました。これらは React の既存の挙動とより合致しており、また追加の情報を有していることがあります。 - キャプチャフェーズのイベント(
onClickCapture
など)は本物のブラウザのキャプチャフェーズのリスナを使うようになります。
これらの変更は React の挙動をブラウザの挙動に近づけて相互運用性を改善するものです。
補足
React 17 は
onFocus
イベント用にfocus
ではなくfocusin
を使うよう裏で変更されましたが、バブリングの挙動に影響はないということに注意してください。React においてonFocus
イベントは常にバブリングしていましたし、React 17 でも同様です。通常はこれがより有用なデフォルトの動作です。この sandbox で特定のユースケースのために加えることのできるチェックについて見ることができます。
イベントプーリングの廃止
React 17 では「イベントプーリング」による最適化が取り除かれています。モダンブラウザではパフォーマンス向上にならず、経験のある React ユーザですらこの挙動に混乱していました:
function handleChange(e) {
setData(data => ({
...data,
// This crashes in React 16 and earlier:
text: e.target.value
}));
}
上記の問題が起こるのは、React が古いブラウザでのパフォーマンス改善のために複数の異なるイベント間でイベントオブジェクトを再利用しており、その際にイベントのフィールドを null
にセットしていたためです。React 16 以前では、イベントを正しく使うために e.persist()
を呼ぶか、本来必要になるより先にプロパティを読み出しておく必要がありました。
React 17 では、上記のコードは本来期待される通りに動作するようになります。古いイベントプーリングによる最適化は完全に取り除かれており、必要なときにいつでもイベントのフィールドを読み出せるようになります。
これは振る舞いの変化であるため破壊的変更としてマークしてありますが、実際上は Facebook 内でこれにより壊れたものは何もありませんでした(むしろ幾つかのバグがいつの間にか修正されたかもしれません!)。e.persist()
は React イベントオブジェクト内に残りますが、今後は何もしなくなります。
副作用クリーンアップのタイミング
useEffect
のクリーンアップ用関数のタイミングをより一貫性のあるものにしました。
useEffect(() => {
// This is the effect itself.
return () => { // This is its cleanup. };});
ほとんどの副作用は画面の更新を遅延させる必要がないので、React は副作用を画面に更新が反映された直後に非同期的に実行します(ツールチップのサイズ測定や位置合わせなど、副作用が画面の更新をブロックする必要がある稀なケースでは、useLayoutEffect
を使うべきです)。
しかし、コンポーネントがアンマウントされる際、副作用のクリーンアップ関数の方は同期的に実行されていました(クラスコンポーネントでの componentWillUnmount
が同期的であるのと同様です)。これは大きなアプリでは望ましくないということが分かりました。大きな画面遷移(タブの切り替えなど)がある場合に遅くなってしまうのです。
React 17 では、副作用のクリーンアップ関数が常に非同期的に実行されます。例えば、コンポーネントがアンマウントされる時、クリーンアップ関数は画面が更新された後で実行されます。
これは副作用の本体側がどのように実行されるかに合わせたものです。同期的に実行されることに依存しているような稀なケースでは、useLayoutEffect
に変更することができます。
補足
アンマウントされたコンポーネントにおける
setState
の警告を修正できなくなってしまうのではと心配されているかもしれません。心配は要りません。React はこのケースに対して特別なチェックを行っており、アンマウントとクリーンアップとの間の小さな時間帯にsetState
の警告を発生させないようになっています。リクエストやインターバルをキャンセルするためのコードはほぼ常に変えずに済みます。
加えて、React 17 は(全コンポーネントにわたる)すべてのクリーンアップ関数を、新しい副作用より前に実行するようになります。React 16 ではコンポーネント内でのみこの順番が保証されているに過ぎませんでした。
起きる可能性のある問題
この変更により動作しなくなったコンポーネントは数個のみでしたが、再利用可能なライブラリではより注意深くテストする必要があるでしょう。問題を引き起こすコードの例は以下のようなものです:
useEffect(() => {
someRef.current.someSetupMethod();
return () => {
someRef.current.someCleanupMethod();
};
});
問題は someRef.current
は書き換え可能であるため、クリーンアップ関数が実行される段階では null
に変わっている可能性がある、ということです。解決方法としては、副作用の内部で書き換わる可能性のある値をキャプチャしておきます:
useEffect(() => {
const instance = someRef.current;
instance.someSetupMethod();
return () => {
instance.someCleanupMethod();
};
});
我々の eslint-plugin-react-hooks/exhaustive-deps
lint ルール(是非使うようにしましょう!)は常にこれを警告してきましたので、このような問題はあまり起きないと思います。
Undefined を返した場合の一貫性のあるエラー
React 16 以前から、コンポーネントが undefined
を返すことは常に間違いでした:
function Button() {
return; // Error: Nothing was returned from render
}
こうなっている理由の一部は、うっかり undefined
を返してしまいがちだから、というものです:
function Button() {
// We forgot to write return, so this component returns undefined.
// React surfaces this as an error instead of ignoring it.
<button />;
}
これまで、クラスおよび関数コンポーネントではこのチェックを行っていましたが、forwardRef
と memo
コンポーネントではこのような返り値のチェックを行っていませんでした。これは我々のコーディングミスによるものです。
React 17 では、forwardRef
と memo
によるコンポーネントの振る舞いが通常の関数・クラスコンポーネントの振る舞いと合致するようになります。これらから undefined
を返すことはエラーになります。
let Button = forwardRef(() => {
// We forgot to write return, so this component returns undefined.
// React 17 surfaces this as an error instead of ignoring it.
<button />;
});
let Button = memo(() => {
// We forgot to write return, so this component returns undefined.
// React 17 surfaces this as an error instead of ignoring it.
<button />;
});
意図的に何もレンダーしたくないという場合には、null
を返すようにしてください。
ネイティブのコンポーネントスタック
ブラウザであなたがエラーをスローすると、ブラウザは JavaScript の関数名とそれらの位置を含んだスタックトレースを表示します。しかし、問題を診断するのに JavaScript のスタックトレースでは不十分で React ツリーの階層構造も同じくらい重要だ、ということがよくあります。Button
がエラーをスローしたということだけでなく、その Button
が React ツリーのどこにあるのかが知りたいでしょう。
この問題を解決するために React 16 で、エラーがあった場合に「コンポーネントスタック」を表示するようにしました。しかし、これはネイティブの JavaScript スタックと比べて劣ったものでした。具体的には、React はソースコード内のどこにその関数が宣言されているのか分からないため、コードをクリックすることができませんでした。また、本番モードではほぼ使いものにならないという問題もありました。minify された JavaScript におけるスタックはソースマップさえあれば自動的に元の関数名に戻せますが、React コンポーネントのスタックにおいては、本番モードでも使えるようにするのかバンドルサイズを小さくするのか選ばなければいけませんでした。
React 17 では、コンポーネントスタックの生成方法が別のメカニズムに変更され、ネイティブ JavaScript スタックと繋ぎ合わせて表示されるようになりました。これにより、本番環境においても完全に名前付きで React のコンポーネントスタックトレースを得られるようになります。
React がこれを実装している方法はいくぶん特殊なものです。現在のところ、ブラウザは関数のスタックフレーム(ソースファイルと位置)を取得する方法を提供していません。このため、React がエラーをキャッチした場合、可能な場合は React は上流にあるコンポーネントのそれぞれから一時エラーをスロー(およびキャッチ)することでコンポーネントスタックを再構成するようになります。これによりクラッシュ時に小さなパフォーマンス低下が起きますが、コンポーネントの型につき 1 度のみです。
興味があれば詳細についてこのプルリクエストで見ることができますが、このメカニズムそのものは、ほぼあなたのコードに影響しません。開発者側から見てこの新機能が意味するのは、コンポーネントスタックをクリック可能になったということ(ネイティブのブラウザのスタックフレームに依存するようになったため)と、本番環境でもそれを普通の JavaScript エラーと同様に読めるようになった、ということです。
これが破壊的変更となっているのは、これを実現するために、React がエラー捕捉後にスタックの上流にある React 関数や React クラスコンストラクタのうちいくつかを再実行する必要があるからです。レンダー関数やクラスコンストラクタは副作用を持つべきではないため(これはサーバレンダリングにおいても重要です)、実際上の問題は起きないはずです。
プライベートなエクスポートの削除
ここで言及すべき最後の破壊的変更は、他のプロジェクトのために公開されていた React の内部構造を一部削除した、ということです。特に、React Native for Web はイベントシステムの内部実装の一部に依存していたのですが、そのような依存は不安定であり、実際によく壊れていました。
React 17 では、このようなプライベートなエクスポートが削除されています。我々の知る限り、React Native for Web がそれを使っていた唯一のプロジェクトであり、既にプライベートなエクスポートに依存しない別の手法にコードの移行を完了しています。
つまり React Native for Web の古いバージョンは React 17 で動作せず、新しいバージョンのみが動作するということです。実際には、React 内部の実装の変化に対応するため React Native for Web は何にせよ新バージョンをリリースする必要があった訳で、大きく話が変わるものではないでしょう。
加えて、ReactTestUtils.SimulateNative
ヘルパメソッドも削除しています。これはドキュメントされたこともなく、名前から期待される通りの動作をしたこともなく、イベントシステムに我々が加えた変更によりうまく動作しなくなりました。テストでネイティブのブラウザイベントを発生させる便利な方法が欲しい場合は、代わりに React Testing Library をチェックしてください。
インストール
React 17.0 リリース候補を試してみて、移行作業中に遭遇する問題について issue を報告してください。リリース候補版は安定リリースと比べてバグがある可能性が高いため、本番環境への投入はまだしないでください。
React 17 RC を npm でインストールするには以下のようにします:
npm install react@17.0.0-rc.3 react-dom@17.0.0-rc.3
React 17 RC を Yarn でインストールするには以下のようにします:
yarn add react@17.0.0-rc.3 react-dom@17.0.0-rc.3
CDN 経由で React の UMD ビルドも提供しています:
<script crossorigin src="https://unpkg.com/react@17.0.0-rc.3/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17.0.0-rc.3/umd/react-dom.production.min.js"></script>
詳細なインストール手順についてはドキュメントを参照してください。
Changelog
React
- Add
react/jsx-runtime
andreact/jsx-dev-runtime
for the new JSX transform. (@lunaruan in #18299) - Build component stacks from native error frames. (@sebmarkbage in #18561)
- Allow to specify
displayName
on context for improved stacks. (@eps1lon in #18224) - Prevent
'use strict'
from leaking in the UMD bundles. (@koba04 in #19614) - Stop using
fb.me
for redirects. (@cylim in #19598)
React DOM
- Delegate events to roots instead of
document
. (@trueadm in #18195 and others) - Clean up all effects before running any next effects. (@bvaughn in #17947)
- Run
useEffect
cleanup functions asynchronously. (@bvaughn in #17925) - Use browser
focusin
andfocusout
foronFocus
andonBlur
. (@trueadm in #19186) - Make all
Capture
events use the browser capture phase. (@trueadm in #19221) - Don’t emulate bubbling of the
onScroll
event. (@gaearon in #19464) - Throw if
forwardRef
ormemo
component returnsundefined
. (@gaearon in #19550) - Remove event pooling. (@trueadm in #18969)
- Stop exposing internals that won’t be needed by React Native Web. (@necolas in #18483)
- Attach all known event listeners when the root mounts. (@gaearon in #19659)
- Disable
console
in the second render pass of DEV mode double render. (@sebmarkbage in #18547) - Deprecate the undocumented and misleading
ReactTestUtils.SimulateNative
API. (@gaearon in #13407) - Rename private field names used in the internals. (@gaearon in #18377)
- Don’t call User Timing API in development. (@gaearon in #18417)
- Disable console during the repeated render in Strict Mode. (@sebmarkbage in #18547)
- In Strict Mode, double-render components without Hooks too. (@eps1lon in #18430)
- Allow calling
ReactDOM.flushSync
during lifecycle methods (but warn). (@sebmarkbage in #18759) - Add the
code
property to the keyboard event objects. (@bl00mber in #18287) - Add the
disableRemotePlayback
property forvideo
elements. (@tombrowndev in #18619) - Add the
enterKeyHint
property forinput
elements. (@eps1lon in #18634) - Warn when no
value
is provided to<Context.Provider>
. (@charlie1404 in #19054) - Warn when
memo
orforwardRef
components returnundefined
. (@bvaughn in #19550) - Improve the error message for invalid updates. (@JoviDeCroock in #18316)
- Exclude forwardRef and memo from stack frames. (@sebmarkbage in #18559)
- Improve the error message when switching between controlled and uncontrolled inputs. (@vcarl in #17070)
- Keep
onTouchStart
,onTouchMove
, andonWheel
passive. (@gaearon in #19654) - Fix
setState
hanging in development inside a closed iframe. (@gaearon in #19220) - Fix rendering bailout for lazy components with
defaultProps
. (@jddxf in #18539) - Fix a false positive warning when
dangerouslySetInnerHTML
isundefined
. (@eps1lon in #18676) - Fix Test Utils with non-standard
require
implementation. (@just-boris in #18632) - Fix
onBeforeInput
reporting an incorrectevent.type
. (@eps1lon in #19561) - Fix
event.relatedTarget
reported asundefined
in Firefox. (@claytercek in #19607) - Fix “unspecified error” in IE11. (@hemakshis in #19664)
- Fix rendering into a shadow root. (@Jack-Works in #15894)
- Fix
movementX/Y
polyfill with capture events. (@gaearon in #19672) - Use delegation for
onSubmit
andonReset
events. (@gaearon in #19333) - Improve memory usage. (@trueadm in #18970)
React DOM Server
- Make
useCallback
behavior consistent withuseMemo
for the server renderer. (@alexmckenley in #18783) - Fix state leaking when a function component throws. (@pmaccart in #19212)
React Test Renderer
- Improve
findByType
error message. (@henryqdineen in #17439)
Concurrent Mode (Experimental)
- Revamp the priority batching heuristics. (@acdlite in #18796)
- Add the
unstable_
prefix before the experimental APIs. (@acdlite in #18825) - Remove
unstable_discreteUpdates
andunstable_flushDiscreteUpdates
. (@trueadm in #18825) - Remove the
timeoutMs
argument. (@acdlite in #19703) - Disable
<div hidden />
prerendering in favor of a different future API. (@acdlite in #18917) - Add
unstable_expectedLoadTime
to Suspense for CPU-bound trees. (@acdlite in #19936) - Add an experimental
unstable_useOpaqueIdentifier
Hook. (@lunaruan in #17322) - Add an experimental
unstable_startTransition
API. (@rickhanlonii in #19696) - Using
act
in the test renderer no longer flushes Suspense fallbacks. (@acdlite in #18596) - Use global render timeout for CPU Suspense. (@sebmarkbage in #19643)
- Clear the existing root content before mounting. (@bvaughn in #18730)
- Fix a bug with error boundaries. (@acdlite in #18265)
- Fix a bug causing dropped updates in a suspended tree. (@acdlite in #18384 and #18457)
- Fix a bug causing dropped render phase updates. (@acdlite in #18537)
- Fix a bug in SuspenseList. (@sebmarkbage in #18412)
- Fix a bug causing Suspense fallback to show too early. (@acdlite in #18411)
- Fix a bug with class components inside SuspenseList. (@sebmarkbage in #18448)
- Fix a bug with inputs that may cause updates to be dropped. (@jddxf in #18515 and @acdlite in #18535)
- Fix a bug causing Suspense fallback to get stuck. (@acdlite in #18663)
- Don’t cut off the tail of a SuspenseList if hydrating. (@sebmarkbage in #18854)
- Fix a bug in
useMutableSource
that may happen whengetSnapshot
changes. (@bvaughn in #18297) - Fix a tearing bug in
useMutableSource
. (@bvaughn in #18912) - Warn if calling setState outside of render but before commit. (@sebmarkbage in #18838)