useSyncExternalStore
昔はuseMutableSourceだったが変わった
https://github.com/reactwg/react-18/discussions/86
略すとuSESになる
React Hook の最終回?
最高のドキュメントがあるのであまり語るべきことはないのだが,「外界から通知された状態変化は常にブロッキングな状態になっていて,非ブロッキングにするには transition 系のプリミティブでオプトインする必要がある」ということだけ覚えておけばなんとかなる
RFC
React Concurrent RenderingにおいてReact外の変数を参照したとき,普通に参照しているとrender phase中にその変数の値が変化してもそれが巻き戻って反映されないので、一貫性が保たれなくなり、まずい
React ReduxではTearingと呼ばれている
外部の変数本体とその「バージョン」を返す関数を組み合わせてMutableSourceとして,それを管理することでこの問題に対処する
注意点
useTransitionによる画面のバージョン分岐が行われているとき、useSyncExternalStoreによって返される値はどちらのバージョンにおいても同じ
当然と言えば当然(Reactの管理範囲外の状態であるため)なのだが、これによって表示上の不整合が生じうることに注意
例えば、historyを使っているとき、locationをuseSyncExternalStoreで取ってくるような実装で今見ているページを表示したりするようにしていると事故る
https://gyazo.com/6a0ec394849da288f26f94248bf0e706
それぞれのボタンをクリック/タップすると遷移して、色が付くよくある(あるか?)表示をイメージしてください
遷移にはuseTransitionが使われ、クリックした瞬間にhistoryは変化するが、画面はデータ取得完了後に遷移するものとする
locationをuseSyncExternalStoreで取るようにしていると、historyが変化した瞬間にlocationが変化してしまうので、画面遷移が完了していないのにボタン表示だけが変化し、結果として整合性が破れる
データの整合性を優先した結果、画面の整合性が破れてしまう
実装
Version Number
render phaseにおいてMutableSourceの値が同じであることを保証するためにrender中に何回MutableSourceのバージョンが変わったのかをカウントする
(理解ができてない)
react-reconcilerはprimary/secondaryのrendererをサポートしている
これをサポートするため、カウンタをprimary/secondaryの2つ用意する
Like Context
と書いてあるので、ContextがConcurrent Modeでどう機能するのか調べないといけなさそう
Version Numberをいつリセットするのかや、どこに格納するのかなどが書いてあるが、本当に実装者しか知らなくていい情報っぽい
Pending update expiration times
Pending update expiration times(Reactにスケジュールされている状態更新の反映期限?)を追跡し、新たにマウントされたコンポーネントが同じソースを読んでいる前に描画されたコンポーネントとのデータ競合の危険なくソースを読めるようにする(?)
たぶん既にrenderが終わっているが画面に反映されていないfiberが"pending updates"なのだと思われる。pending updatesの反映期限が今renderしているfiberの反映期限より先であれば、(Version Numberが変わっていない限り)値が食い違うことはない。だめだったらやり直す(どこから?)
追記
最近のアプデでunstable_プレフィックスが付いたらしい(そもそも今まで付いてなかったほうがおかしい気もするが)