🍉

useLayoutEffectてなんぞや。と今更思った話。

2023/05/06に公開

概要

前回の投稿useEffectのクリーンナップについて調べている時に、ふとuseLayoutEffectというhookを目にしました。
Reactの学習を始めた頃になんか見たことあるなと思っていたが、使い方や、useEffectとの違いが分からなかったので、少し調べてみた際のメモです。

useLayoutEffectとuseEffectの違い

https://react.dev/reference/react/useLayoutEffect

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>
  );

ちらつき部分

  1. 最初の描画で一瞬、countに0が入り<p>{count}</p>には0が入るが、その後useEffectで1~100のランダムな整数がcountに入り<p>{count}</p>が変わる。
  2. 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の流れについては以下のようになる。多分。

  1. まず、countが0の状態でレンダリングする。
  2. Reactはそれをdom(仮想)に配置し、ブラウザレンダリングを止める。
  3. useLayoutEffectを実行し、すぐに再レンダリングを実行する。(仮想domを生成)
  4. ブラウザのレンダリング。画面に描画される。



結論、useEffectとuseLayoutEffectの違いは、ブラウザレンダリングの後に非同期的に実行するか、前に同期的に実行するかの違いである。

使いどころ

公式にもあるが、パフォーマンスの観点から、基本的にはuseEffectを使うことが推奨されている。

useLayoutEffectの使い所としては、
DOMの読み取りや書き込み、描画に関連する処理を扱う場合。 例えば、要素のサイズや位置を計算し、アニメーションを行う場合や、コンポーネントのマウント時にスクロール位置を調整するなど。

ブラウザが描画を行う前に副作用が実行されるため、useLayoutEffect内で非同期処理を行うことは推奨されない。そのため、データの取得、購読などはuseEffectを使うようにすることが推奨される。


総合的に、どちらのフックを使用するかは、副作用のタイプやパフォーマンスへの影響を考慮して決定する。
パフォーマンスを考慮しつつ、
基本的に「描画」に関する副作用はuseLayoutEffectでそれ以外はuseEffectとする。

参考
公式

Discussion