useLayoutEffectのdocsを読む
これです
useLayoutEffect is a version of useEffect that fires before the browser repaints the screen.
らしい
Reference
useLayoutEffect(setup, dependencies?)
'a version of useEffect that ...' ということでuseEffectでありますけれども、
The code inside useLayoutEffect and all state updates scheduled from it block the browser from repainting the screen. When used excessively, this makes your app slow. When possible, prefer useEffect.
という一点には注意する。なるほど、たしかにユーザーに画面を早く届けるためには、とりま画面更新→Effectの計算となるわけだが、画面更新される前に計算しておきたいものはuseLayoutEffect
を使う必要があって、計算量次第ではユーザー体験下げるから気をつけてねってことね。
Usage
Tooltipの表示位置を計算する例が載っている。
計算手順Deepl訳
- ツールチップは、初期のtooltipHeight = 0でレンダリングされます(そのため、ツールチップの位置がおかしくなる可能性があります)。
- ReactはこれをDOMに配置し、useLayoutEffectのコードを実行します。
- useLayoutEffectは、ツールチップコンテンツの高さを測定し、即座に再レンダリングをトリガーします。
- ツールチップは、実際のtooltipHeightで再レンダリングされます(ツールチップが正しく配置されるように)。
- ReactはDOM内のそれを更新し、ブラウザは最終的にツールチップを表示します。
Troubleshooting
Layoutの計算はもちろんユーザークライアント側でしか計算できないので、サーバーでもレンダリング時にはできず、しばしばHydration errorが起こる。
いくつかの対策がある。
Replace useLayoutEffect with useEffect. This tells React that it’s okay to display the initial render result without blocking the paint (because the original HTML will become visible before your Effect runs).
RadixはSSR対応としてuseEffctを選んでいたのかもしれない?
Alternatively, mark your component as client-only. This tells React to replace its content up to the closest <Suspense> boundary with a loading fallback (for example, a spinner or a glimmer) during server rendering.
client-only
にするとSSR時にはSuspendするようになってるのふつうに知らんかった
Suspenseもはよ読みたいよなあ
Alternatively, you can render a component with useLayoutEffect only after hydration.
isMountedみたいなStateを持つ対策方法
If you synchronize your component with an external data store and rely on useLayoutEffect for different reasons than measuring layout, consider useSyncExternalStore instead which supports server rendering.
useSyncExternalStoreはsupports server rendering.らしい。正味使い方知らない。Effectシリーズとして次はこれを読みに行きたい。