useLayoutEffect and SSR
How to get warning free usage of useLayoutEffect when server-side rendering
useEffect
and useLayoutEffect
are React
hooks that allow for the creation of side effects. They are a replacement for thecomponentDidMount
, componentDidUpdate
and componentWillUnmount
lifecycle methods on class
components.
Kent C. Dodds has put together a great guide on when to use useEffect
and when to use useLayoutEffect.
TLDR: most of the time you will want useEffect
useLayoutEffect
is put forward as the safest upgrade path for class
components
useLayoutEffect
fires in the same phase ascomponentDidMount
andcomponentDidUpdate
— React Documentation
However, if you use useLayoutEffect
then you will get a nasty SSR console
warning
function App() {
useLayoutEffect(() => {
console.log("layout effect");
});
return "hello world";
}// Will log a warning
const html = ReactDOMServer.renderToString(<App />);
⚠️ 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. See https://fb.me/react-uselayouteffect-ssr for common fixes.
Even though useLayoutEffect
does nothing when using renderToString
it will log a big old good time warning. Personally, I am not a fan of this behaviour given that useLayoutEffect
is the safest upgrade path for class
components. However, I can see the argument that for most people they should be using useEffect
and this pushes them in that direction. But I still think logging a warning is too much.
A workaround (hack)
Create useIsomorphicLayoutEffect
// use-isomorphic-layout-effect.jsimport { useLayoutEffect, useEffect } from 'react';const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;export default useIsomorphicLayoutEffect;
Use instead of useLayoutEffect
// app.js
import useLayoutEffect from './use-isomorphic-layout-effect';function App() {
useLayoutEffect(() => {
console.log('hello there');
}, []);
return 'Hello world';
};
No more SSR warnings! 😊
In this technique (hack), we conditionally return useLayoutEffect
or useEffect
based on whether we are running in the browser or not. Both useEffect
and useLayoutEffect
have the same API so your type system should be all good.
This hack is currently being used in react-redux
and react-beautiful-dnd
.
eslint
eslint-plugin-react-hooks
is a really valuable eslint
plugin if you are using hooks. In order for our custom use-isomorphic-layout-effect
to correctly leverage the rules that the plugin has for useLayoutEffect
, your import
must be named useLayoutEffect
. You can either do this by using a named export inuse-isomorphic-layout-effect
import { useLayoutEffect, useEffect } from 'react';const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;// Ensure the name used in components is useLayoutEffect
export default { useLayoutEffect: useIsomorphicLayoutEffect };
Or you can use eslint
to force the name of the default export from use-isomorphic-layout-effect
to be useLayoutEffect
.
module.exports = {
// other config ...rules: {
'no-restricted-imports': [
'error',
{
// Disabling using of useLayoutEffect from react
{
name: 'react',
importNames: ['useLayoutEffect'],
message:
'`useLayoutEffect` causes a warning in SSR. Use `useIsomorphicLayoutEffect`',
},
],
},
],
'no-restricted-syntax': [
'error',
// Ensure import from '*use-isomorphic-layout-effect' is `useLayoutEffect` to leverage `eslint-plugin-react-hooks`
{
selector:
'ImportDeclaration[source.value=/use-isomorphic-layout-effect/] > ImportDefaultSpecifier[local.name!="useLayoutEffect"]',
message:
'Must use `useLayoutEffect` as the name of the import from `*use-isomorphic-layout-effect` to leverage `eslint-plugin-react-hooks`',
},
],
}
}
Summary
Most of the time you will be using useEffect
. useLayoutEffect
logs a nasty warning in server-side rendering. You can hack around this warning by conditionally using useEffect
in server environments. Boom.