useLayoutEffectてなんぞや。と今更思った話。
概要
前回の投稿でuseEffect
のクリーンナップについて調べている時に、ふとuseLayoutEffect
というhookを目にしました。
Reactの学習を始めた頃になんか見たことあるなと思っていたが、使い方や、useEffect
との違いが分からなかったので、少し調べてみた際のメモです。
useLayoutEffectとuseEffectの違い
useLayoutEffect
も基本的にはuseEffect
と使い方は変わりません。
useLayoutEffect
は、ブラウザが画面を再描画する前に実行する、useEffect
のバージョンです。
構文 : useLayoutEffect(setup, dependencies?)
違い
useEffect
useEffect
は、コンポーネントのレンダリングが完了した後に非同期的に副作用が実行され、 通常、データの取得や購読のセットアップ、クリーンアップなどの副作用を扱う場合に使われるが、useEffect はブラウザの描画が終わってから実行されるため、レイアウトや描画に関連する処理を行うと、画面のちらつきなどが発生する可能性がある。
例えば以下のようにdomを変更するuseEffectの場合、画面のちらつきが発生する。
const [count, setCount] = useState(0);
const onClickZero = () => setCount(0);
useEffect(() => {
if (count === 0) setCount(Math.floor(Math.random() * (100 - 1 + 1)) + 1)
}, [count])
return (
<div>
<p>{count}</p>
<button onClick={onClickZero}>0にする</button>
</div>
);
ちらつき部分
- 最初の描画で一瞬、countに0が入り
<p>{count}</p>
には0が入るが、その後useEffectで1~100のランダムな整数がcountに入り<p>{count}</p>
が変わる。 - onClickでcountが0になり一瞬、
<p>{count}</p>
に0が入るが、すぐにランダムな整数に変わる
上記のようなちらつきは、画面の描画(ブラウザレンダリング)が完了してから、副作用が実行されるというuseEffect
の挙動によるものである。
useLayoutEffect
同期的に実行され、ブラウザの描画の前に副作用が実行される。 これにより画面のちらつきなどを防ぐことができるが、同期的に実行されるという性質からパフォーマンスに影響を与える可能性がある。
先ほどのuseEffectの部分をuseLayoutEffectに書き直すと、ブラウザの描画の前に、ランダム整数が生成されるため、画面のちらつきはなくなる。
useLayoutEffect(() => {
if (count === 0) setCount(Math.floor(Math.random() * (100 - 1 + 1)) + 1)
}, [count])
Reactが指すレンダリングの流れに関わることだが、useLayoutEffect
の流れについては以下のようになる。多分。
- まず、countが0の状態でレンダリングする。
- Reactはそれをdom(仮想)に配置し、ブラウザレンダリングを止める。
-
useLayoutEffect
を実行し、すぐに再レンダリングを実行する。(仮想domを生成) - ブラウザのレンダリング。画面に描画される。
結論、useEffectとuseLayoutEffectの違いは、ブラウザレンダリングの後に非同期的に実行するか、前に同期的に実行するかの違いである。
使いどころ
公式にもあるが、パフォーマンスの観点から、基本的にはuseEffectを使うことが推奨されている。
useLayoutEffect
の使い所としては、
DOMの読み取りや書き込み、描画に関連する処理を扱う場合。 例えば、要素のサイズや位置を計算し、アニメーションを行う場合や、コンポーネントのマウント時にスクロール位置を調整するなど。
ブラウザが描画を行う前に副作用が実行されるため、useLayoutEffect
内で非同期処理を行うことは推奨されない。そのため、データの取得、購読などはuseEffect
を使うようにすることが推奨される。
総合的に、どちらのフックを使用するかは、副作用のタイプやパフォーマンスへの影響を考慮して決定する。
パフォーマンスを考慮しつつ、
基本的に「描画」に関する副作用はuseLayoutEffect
でそれ以外はuseEffect
とする。
参考
公式
Discussion