React provides us with a useLayoutEffect hook to improve our applications in some specific cases, however not everyone is aware of how to use it.
It works pretty much the same as useEffect, but they are executed after different stages in the rendering cycle of a component.
This hook is synchronous, meaning that React waits for all the logic within the hook to complete and execute immediately after the rendering phase, but before drawing the changes on the screen.
Consider the following example:
const App = () => {
useLayoutEffect(() => {
console.log("useLayoutEffect");
}, []);
useEffect(() => {
console.log("useEffetct");
}, []);
console.log("Render");
return <div>Hello, world!</div>;
};
The output:
The sequence is the following:
The hook becomes very useful when you see flickering on an initial render, as in the following example:
const App = () => {
const [user, setUser] = useState("Unnamed");
useEffect(() => {
setUser("John");
}, []);
return <div>Hello, {user}!</div>;
};
The output:
The flickering is caused by setting a user name just after the changes have been printed.
To avoid this, refactor the above example with useLayoutEffect:
const App = () => {
const [user, setUser] = useState("Unnamed");
useLayoutEffect(() => {
setUser("John");
}, []);
return <div>Hello, {user}!</div>;
};
The output:
Note that we don't see flickering because se set the user name before outputting the changes to the screen.
Important Note: Remember that useLayoutEffect is synchronous, so React will wait until all logic in this hook is executed to continue painting, which can cause performance issues. Therefore, 99% of the time you should be fine with useEffect.
Consider the following example:
const App = () => {
const [user, setUser] = useState("Unnamed");
useEffect(() => {
for (let i = 0; i < 1000000000; i++) {
if (i === 999999999) {
setUser("John");
}
}
}, []);
return <div>Hello, {user}!</div>;
};
The output:
Text rendering is blocked until the time-consuming operation in useLayoutEffect is finished.
The useLayoutEffect is executed synchronously before the changes are drawn to the screen.
It's useful for preventing flickering on the first render, but can easily be overused, so be careful.
In 99% of cases you should be fine with useEffect, so always try it first and see if it works well for you, as most effects don't need UI to wait until they are finished.