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
そもそもuseEffectとuseLayoutEffectの違いは何か。
公式のドキュメントによると、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に反映されたのを待ってから実行される。
useEffectもuseLayoutEffectも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>
)
}