外部ストア購読の問題
concurrent Reactで、tearingが発生する仕組みと、useStateやuseRefによる外部ストアの購読が安全でない理由を理解します。
「外部ストア購読の問題」はCoddyKit上の無料React Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはReact Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 React Academyコースには全4レッスンが含まれています。
外部ストアとは
外部ストアとは、Reactツリーの外部に存在する状態のあらゆるソースです。たとえばReduxストア、matchMediaのようなブラウザーAPI、独自のsubscribe機構を持つ単純なモジュールレベルのオブジェクトなどがあります。
Reactはこの状態を所有していないため、ストアが現在保持している値とコンポーネントを同期させるには、変更を購読してスナップショットを読み取る必要があります。
素朴なuseState + useEffectサブスクリプション
よくある方法は、ストアの値をuseStateに取り込み、その後useEffectを使ってストアの変更を購読し、変更のたびにsetStateを呼び出すことです。一見すると、これでコンポーネントを最新の状態に保てるように思えます。
単純な同期レンダリングでは機能しますが、Reactが並行してレンダリングし、処理を途中で一時停止または再開できるようになると、微妙な問題が明らかになります。
ティアリング問題
ティアリングは、1回のレンダーパス中に、UIの異なる部分が同じストアの異なる値を表示すると発生します。並行レンダリングでは処理が中断・再開される可能性があり、その間にストアが変更されることがあります。
その結果、一方のコンポーネントが古い値を表示し、別のコンポーネントが新しい値を表示する一貫性のない画面になります。並行機能が登場する以前には、このような不整合は事実上起こりませんでした。
例:レンダリング途中のダークモードによるティアリング
Reactが並行レンダリングしている間に、多くのコンポーネントがテーマストアを読み取っている状況を考えてみましょう。途中でユーザーがダークモードを切り替えると、切り替え前にレンダリングされたコンポーネントはライトテーマを表示し、切り替え後のコンポーネントはダークテーマを表示します。
Reactがストアの値の変更をアトミックなものとして扱わなかったため、1フレームの間、画面の半分がライト、半分がダークという状態になります。
useEffectでは遅すぎる理由
エフェクトはレンダリングのコミット後に実行されるため、useEffectベースのサブスクリプションでは、レンダリング中に読み取った値がコミット時のストアの値と一致することを保証できません。不整合のある描画を防ぐには、サブスクリプションの実行が遅すぎます。
エフェクトが再購読して状態を更新する頃には、不整合のあるフレームがすでにユーザーに表示されている可能性があります。
非推奨のuseMutableSource
useMutableSourceという以前の実験的なAPIは、並行レンダリングに安全な外部ストアの読み取りを解決しようとしました。しかし、APIを正しく使うのが難しく、安定版としてリリースされることはありませんでした。
Reactチームはこれを、同じ一貫性の保証を保ちながら、扱いにくさを減らした、よりシンプルで使いやすいプリミティブに置き換えました。
解決策としてのuseSyncExternalStore
useSyncExternalStoreは、外部ストアを並行レンダリングに安全な方法で購読するための公式フックです。Reactと連携して、レンダリング中の読み取りの一貫性を保ち、コンポーネントでティアリングが発生しないようにします。
並行レンダリング下で外部状態をReactに確実に橋渡しする必要があるライブラリ作者にとって、推奨されるプリミティブになりました。
影響を受けたライブラリ
外部ストアを購読する状態管理ライブラリは直接影響を受けました。Redux、MobX、Valtioなどがその例です。並行機能が提供されても安全性を保てるよう、それぞれが新しいプリミティブを採用する必要がありました。
そのため、これらのReactバインディングは、以前のuseStateとuseEffectを組み合わせるパターンに頼るのではなく、内部でuseSyncExternalStoreを呼び出すように更新されました。
一貫したスナップショットと並行レンダリングの安全性
このフックを使うと、レンダー中にストアを読み取るすべてのコンポーネントが同じスナップショットを参照できます。また、ストアが変更され、そのままではティアリングが発生する場合は、同期的に再レンダーを行います。
一貫したスナップショットと並行レンダリングの安全性を両立できることが、独自にサブスクリプションを実装する場合と比べた、このフックの最大の価値です。
将来の変更にも対応できるプリミティブ
useSyncExternalStoreを使うと、Reactのレンダリングモデルが進化しても、サブスクリプションを正しく維持できます。自分で不完全に再実装するのではなく、フレームワークが提供する保証を利用できます。
そのため、外部の信頼できるデータソースをReactコンポーネントに接続するための、堅牢で推奨される方法となっています。
クイックチェック:ティアリングの問題
並行レンダリングにおけるティアリングの意味を確認してください。
復習:外部ストアのサブスクリプションが抱える問題
外部ストアはReactの外部に存在するため、単純なuseStateとuseEffectによるサブスクリプションでは、レンダリングが並行して行われるとティアリングを防げません。エフェクトは実行されるタイミングが遅く、1回のレンダー中に読み取る値の一貫性を保てないためです。
useSyncExternalStoreは、使われなくなったuseMutableSourceに代わるもので、一貫したスナップショットと並行レンダリングの安全性を提供します。Redux、MobX、Valtioなどのライブラリは、ストアのサブスクリプションにおける将来の変更にも対応できるプリミティブとして、これを採用しています。
よくある質問
「外部ストア購読の問題」レッスンは無料ですか?
はい。「外部ストア購読の問題」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、React Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 React Academyコースには全4レッスンが含まれています。
「外部ストア購読の問題」で何を学びますか?
concurrent Reactで、tearingが発生する仕組みと、useStateやuseRefによる外部ストアの購読が安全でない理由を理解します。 ブラウザで直接実行するハンズオンコードでReact Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
React Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのReact Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「外部ストア購読の問題」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このReact Academyレッスンでコードを書いて実行できますか?
はい。すべてのReact Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。