useLayoutEffectを使った時のSSR warning

May 28, 2021

programming
article cover
Image by Sam Moqadam

Warning

GatsbyのようなSSG(Static Site Generator)でuseLayoutEffectを使っていると次のようなwarningに出会す。

Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer’s output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client

簡単に言うとuseLayoutEffectはSSR時に何もしないので、clientサイドでのみ実行されるようにしておけ、ということらしい。

useEffect & useLayoutEffect

そもそもuseEffectuseLayoutEffectの違いは何か。
公式のドキュメントによると、useEffectは次のように定義されている。

The function passed to useEffect will run after the render is committed to the screen.

普段Reactを使っている人にはこの辺りの説明は不要だと思う。

ではuseLayoutEffectとは何か、公式ドキュメントによると、

The signature is identical to useEffect, but it fires synchronously after all DOM mutations.

つまりuseLayoutEffectはレンダリング処理が終わっただけでなく、その結果がDOMに反映されたのを待ってから実行される。

useEffectuseLayoutEffectもSSR時に何もしないという点では同じだが、useLayoutEffectはDOMに関与するため、上記のような警告が出る。

Solution

Warningなので別に何もしなくてもbuild等に影響はないが、やはり気持ちが悪いので消しておきたい!
これを解決するには、SSR時にはuseEffectを使うように指示して、それ以外の時にはuseLayoutEffectを使うようにしてやればいい。

Gatsby等のSSGでは、次のようにして実際にclientサイドで実行されているかどうか確認することができる。

typeof window !== 'undefined'

これを利用してSSR時にはuseEffectを返し、それ以外はuseLayoutEffectを返すhooksを作る。

import { useEffect, useLayoutEffect } from 'react'

export const useIsomorphicEffect = () => {
  return typeof window !== 'undefined' ? useLayoutEffect : useEffect
}

このhooksを使ってuseEffectを書けば無事warningは消える。

import React, { useEffect, useLayoutEffect } from 'react'
import { useIsomorphicEffect } from './useIsomorphicEffect'

const App = () => {
  const isomorphicEffect = useIsomorphicEffect()
  
  isomorphicEffect(() => {
    // do something you want
  }, [])
  
  return (
    <div>
      Hellow world
    </div>
  )
}

Profile picture

元カメラマン。今はベルリンで働くWEBエンジニア。
スロバキア人の妻とドイツ猫二匹の多国籍家族。