Telerik blogs

The useLayoutEffect and useEffect hooks in React share similarities in function but differ when it comes to execution timing. In this article, we’ll delve into this main difference, providing insights into when and why to use each hook effectively.

In earlier articles, we’ve discussed how the useEffect, useContext and useReducer hooks enhance function component capabilities in React. In particular, we’ve come to see how the useEffect hook allows us to perform side effects in function components, offering an elegant solution to handle tasks like API calls, data fetching and subscriptions.

There’s another core React hook that closely resembles useEffect in both behavior and name: the useLayoutEffect hook. In this article, we’ll explore the intricacies of the useLayoutEffect hook, examining its specific applications and how it differs from the useEffect hook.

useLayoutEffect Hook

The useLayoutEffect hook, just like the useEffect hook, can be used to perform side effects in function components. useLayoutEffect doesn’t return any values but instead takes two arguments. The first being required and the second optional.

  • The first argument is the effect callback function we want the hook to run (i.e., the effect itself).
  • The second (optional) argument is a dependency array that contains dependencies that when changed trigger the effect to re-run.

useLayoutEffect(effect,deps); - effect is the function that is to run imperative and likely effectful code. deps is the dependency array to contain dependencies that when changed trigger the effect to run

In code, useLayoutEffect would be used like the following:

import React, { useLayoutEffect } from "react";

export const FunctionComponent = () => {
  useLayoutEffect(() => {
    // effect callback function
  }, [] /* optional dependencies array */);

  return (
    // ...
  );
};

Just like the useEffect hook, there are three stages of a lifecycle of a React component where we may want to run a side effect:

  • On every render
  • Only on initial render
  • On initial render and anytime a certain dependency changes

For a breakdown of the different lifecycle stages at which an effect can be executed within a component, refer to the previous article titled A Breakdown of the React useEffect Hook.

From initial glance, the useLayoutEffect hook looks identical to the useEffect hook, so what might prompt a developer to choose one over the other in a React application?

Execution Timing

The primary difference between useEffect and useLayoutEffect is when the callback is executed. With useEffect, the callback is executed asynchronously after the component has rendered and the screen has been updated. useLayoutEffect, on the other hand, fires synchronously after all DOM mutations but before the browser has painted the changes. To better understand this, we’ll go through a simple example.

Assume we wanted to log two different messages to the console to observe the order of execution. The first message, “useEffect: DOM updated”, would be placed in the callback function of the useEffect hook, while the other message—"Rendering Component”—would be placed directly in the body of the function component, outside of the useEffect hook.

import React, { useEffect } from "react";

function ExampleComponent() {
  useEffect(() => {
    console.log("useEffect: DOM updated");
  });

  console.log("Rendering Component");

  return <div>Hello World</div>;
}

If we were to test this behavior in the browser, we’ll notice:

  1. The “Rendering Component” console message is logged first during the rendering phase of the component.
  2. The browser paints the screen by placing the “Hello World” message onto the screen.
  3. After the component has finished rendering and the screen has been painted, the effect is run and the “useEffect: DOM updated” message is logged to the console.

We could use the same setup as above but instead leverage the useLayoutEffect Hook:

import React, { useLayoutEffect } from "react";

function ExampleComponent() {
  useLayoutEffect(() => {
    console.log("useLayoutEffect: Before paint");
  });

  console.log("Rendering Component");

  return <div>Hello World</div>;
}

If we were to run the above code example, we would notice a small difference in the sequence of events:

  1. The “Rendering Component” console message is still logged first. This occurs during the rendering phase of the component, just like in the previous example with useEffect.
  2. Then, before the browser gets a chance to paint the “Hello World” message onto the screen (i.e., the screen is still blank), the useLayoutEffect hook fires. This results in the “useLayoutEffect: Before paint” message being logged to the console.
  3. Finally, after these console messages are logged, the browser paints the screen, displaying the “Hello World” message.

The difference in execution timing between useEffect and useLayoutEffect is subtle yet crucial for certain types of operations in a React application. The rendering and painting process happens quickly, often making it hard to observe the difference with the naked eye. However, the key is in understanding the synchronous and asynchronous nature of these hooks.

useLayoutEffect is useful when we need to make changes to the DOM and want to ensure that the changes are visible to the user immediately before they see anything else. This is helpful in:

  • Preventing flicker. Using useLayoutEffect allows you to make DOM changes before the screen is updated. This can prevent flickering or other unwanted visual artifacts when the user sees updates occur on the screen.
  • Ensuring accurate measurements. When we need to measure the DOM (like getting the width of an element), useLayoutEffect ensures we’re measuring the DOM after it’s been updated but before the user sees the changes. This guarantees accuracy without causing a re-render.

useEffect is designed for most common effects, such as data fetching, setting up subscriptions, and manually changing the DOM when React doesn’t need to be aware of the changes. As a result, useEffect should be used when the effect does not impact the layout or visuals before the next paint. Since useEffect is asynchronous and doesn’t block the browser from painting, it can lead to better performance as it allows the browser to prioritize screen updates.

Wrap-up

In wrapping up, the distinction between useEffect and useLayoutEffect in React hinges on their timing of execution. When deciding which hook to use, always opt for the useEffect hook if the effect doesn’t need immediate synchronization with the DOM, such as for API calls or data fetching. This should suffice for most scenarios involving effect execution.

useLayoutEffect is best reserved for more complex operations that require interaction with the DOM or specific timing, such as DOM measurements or preventing visual flickers.

For more additional reading, be sure to check out the official React documentation on the useLayoutEffect hook.


About the Author

Hassan Djirdeh

Hassan is a senior frontend engineer and has helped build large production applications at-scale at organizations like Doordash, Instacart and Shopify. Hassan is also a published author and course instructor where he’s helped thousands of students learn in-depth frontend engineering skills like React, Vue, TypeScript, and GraphQL.

Related Posts

Comments

Comments are disabled in preview mode.