useLayoutEffect vs useEffect

Necati Özmen - Jun 2 '23 - - Dev Community

Author: Wisdom Ekpotu

Introduction

In recent years, React has solidified itself in the ever-changing landscape of web development as one of the most efficient ways of building highly performant and interactive user interfaces. With the introduction of a new feature called Hooks, React has revolutionised the way developers manage stateful and reusable logic within functional components.

With Hooks you can use state and other React features without the need to write ES6 class components. One important hook though not very popular is the useLayoutEffect hook, which allows developers to handle and perform side effects in React components.

In this article, you will explore the useLayoutEffect hook in-depth, how it differs from useEffect and learn how to properly leverage its capabilities to enhance the user experience. Also, to follow along with this article you should have some experience working with Reactjs.

What are side effects in React?

To truly grasp what useLayoutEffect is and what it does, it's essential to have a solid understanding of side effects in React.

A component's primary responsibilities include rendering the user interface (UI), responding to user input and events, and re-rendering the UI as necessary. You might need to carry out some tasks or operations when working on a React project that falls outside the render cycle of the component. These are known as "Side Effects".

A side effect is anything that happens within your application that is not (at least not directly) related to UI rendering. For example, send HTTP requests to servers, store data in the browser's memory, and set time functions. There are no UI changes in these scenarios. In other words, React will not re-render your component for these scenarios.

Although they can be very helpful in our application and are a key concept in functional programming, side effects can also be challenging to manage and, if done incorrectly, can result in unexpected behaviour and performance problems.

To handle side effects you can make use of a set of built-in hooks called Effect Hooks namely; useEffect, useLayoutEffect, useInsertionEffect.

Among these hooks, the useEffect hook is the most used by react developers compared to the other hooks. But a question arises. Is it suitable for treating all kinds of side effects?

The useEffect hook

If you have written React code using class components then you should be familiar with the lifecycle methods; componentDidMount, componentDidUpdate, and componentWillUnmount.

The useEffect hook is a combination of all three lifecycle methods hence it allows us to access lifecycle methods in functional components.

The useEffect hooks runs asynchronously ie and It is commonly used to make API requests.

Syntax:

//src/App.js
import React, { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // Side effect logic goes here
    console.log('Component rendered!');
    // Cleanup function (optional)
    return () => {
      console.log('Component unmounted!');
    };
  }, []); // Empty dependencies array, runs only on mount
  return (
    <div>
      {/* Component JSX */}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useEffect operates once the component is fully loaded initially, and then every time there's a change in the component's state.

What is the problem with useEffect?

As stated above the useEffect hook is asynchronous this has a significant drawback in that it can only be called after the component has been mounted. This implies that side effects that depend on the layout of the component cannot be carried out using useEffect.

Now how do we solve this problem, this is where useLayoutEffect comes in.

Introducing the useLayoutEffect hook?

While many React developers are familiar with the widely used useEffect hook, the useLayoutEffect hook remains overshadowed by its sibling but is still a powerful tool for improving the performance of React apps.

Unlike the useEffect hook, the useLayoutEffect hook runs synchronously which means it runs immediately after React has performed all the necessary DOM mutations but just before the browser paints the screen. It has the same API and possesses a similar syntax as the useEffect hook.

This hook was introduced to solve some layout specific / niche issues that plagued devs when using the useEffect hook.

Syntax:

// src/App.js

import React, { useLayoutEffect } from 'react';

function MyComponent() {
  useLayoutEffect(() => {
    // Perform side effects here
    // This code runs after the component has rendered but before the browser paints the screen

    return () => {
      // Cleanup code here (optional)
    };
  }, []);

  return (
    // JSX code for your component
  );
}
Enter fullscreen mode Exit fullscreen mode

useLayoutEffect is usually used together with the useRef hook, which will allow you to get a reference to any DOM element that you can use to read layout information.

How does the useLayoutEffect hook work

Here is a basic overview of how the useLayoutEffect hook works:

  • The user interacts with the application.
  • The components' states change.
  • After that, the DOM is altered.
  • If the useLayoutEffect dependencies have changed, this method is called to clean up effects from the previous render.
  • After cleanup, the useLayoutEffect hook is called.
  • Changes are reflected on the browser screen.

When should you use the useLayoutEffect hook?

1. Adding Smooth Scroll:

Example:

import React, { useRef, useLayoutEffect } from 'react';

const SmoothScrolling = () => {
  const containerRef = useRef(null);

  useLayoutEffect(() => {
    const container = containerRef.current;

    const handleScroll = () => {
      // Smoothly scroll to the top of the container
      container.scrollTo({
        top: 0,
        behavior: 'smooth',
      });
    };

    // Scroll to the top when the component is mounted
    handleScroll();

    // Add event listener to scroll to the top on subsequent scrolls
    window.addEventListener('scroll', handleScroll);

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);

  return (
    <div ref={containerRef}>
      {/* Your Content */}
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

In the code above, the useLayoutEffect hook is used to add smooth scrolling functionality to a container element. An event listener is set up to listen for a scroll event on the window object and invoke the handlescroll function. The function will smoothly scroll the container to top using the scrollTo method with { top: 0, behavior: 'smooth' } as the options.

The useLayoutEffect hook will perform the initial scroll to the top when the component is mounted. A clean-up function is added to remove the event listener when the component is unmounted.


Open-source enterprise application platform for serious web developers

refine.new enables you to create React-based, headless UI enterprise applications within your browser that you can preview, tweak and download instantly.

🚀 By visually combining options for your preferred ✨ React platform, ✨ UI framework, ✨ backend connector, and ✨ auth provider; you can create tailor-made architectures for your project in seconds. It feels like having access to thousands of project templates at your fingertips, allowing you to choose the one that best suits your needs!


refine blog logo

2. Animating Elements:

Example:

import React, { useRef, useLayoutEffect } from 'react';

const AnimatingElements = () => {
  const elementRef = useRef(null);

  useLayoutEffect(() => {
    const element = elementRef.current;

    // Animate the element's opacity on mount
    element.style.opacity = 0;
    setTimeout(() => {
      element.style.opacity = 1;
    }, 1000);

    return () => {
      // Clean up animation when the component unmounts
      element.style.opacity = 0;
    };
  }, []);

  return <div ref={elementRef}>Animate me!</div>;
};
Enter fullscreen mode Exit fullscreen mode

The code block above demonstrates how to animate an element's opacity using the useLayoutEffect hook. An initial opacity of the element is set to 0 and then a setTimeout function is used to animate it to 1 after a delay of 1000ms.

Then the useLayoutEffect hook applies the animation after the component is mounted. The element's opacity is reset to 0 when the component is unmounted.

3. Auto-focus Input Field:

Example:

import React, { useRef, useLayoutEffect } from 'react';

const AutoFocusInput = () => {
  const inputRef = useRef(null);

  useLayoutEffect(() => {
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} />;
};
Enter fullscreen mode Exit fullscreen mode

In the preceding code, we make use of the useLayoutEffect hook to automatically focus on an input field when the component is mounted.
We proceed to access the input element using the ref hook. Inside the useLayoutEffect hook, we call the focus method on the input element to give it focus. Since we want this only to run once we will leave the dependency array empty ([]).

Note: For this example there is no cleanup function because there is no need to undo the focus when the component is unmounted.

Comparing useEffect to useLayoutEffect

useEffect Hook useLayoutEffect Hook
Order of Execution Runs after rendering and any updates have been applied. Runs after rendering but before the browser paints the screen
Scheduling Schedule asynchronous operations Schedules synchronous operations
Timing Runs asynchronously during the render phase. Runs synchronously during the commit phase.
Use Cases Fetching data, subscribing to events, scheduling side effects. Performing measurements, synchronously modifying the DOM based on layout.
Blocking Nature Non-blocking, does not delay rendering Potentially blocking, may delay rendering
Performance Optimized for performance in most cases Can cause performance issues if not used carefully
Usage Considerations Preferable for most side effects and effects that don't require immediate visual updates. Suitable for effects that need to be applied synchronously before the browser paints.
Dependencies Can specify an array of dependencies to control when the effect runs. Similar to useEffect, dependencies can be specified to optimize effect re-execution.
Server-side Rendering (SSR) Can be used in both client-side and server-side rendering environments. Not recommended for server-side rendering, as it can block rendering. Use useEffect instead.

Benefits of using the useLayoutEffect hook

  • It ensures that layouts are very consistent throughout and are stable before the user sees it.
  • It helps prevent unnecessary re-renders or repaints by synchronizing state changes with DOM changes.
  • Preventing Flickering or unwanted content flashes: In some circumstances, utilizing useLayoutEffect can assist to eliminate visual flickering or layout shifts that might occur when elements need to be relocated or styled depending on layout information. By performing the appropriate layout changes synchronously before the browser paints, you may prevent the visual glitches that could arise if you used useEffect and had a delay between layout changes and rendering.

Pitfalls of using the useLayoutEffect hook

  1. A major pitfall of this hook according to the official React docs, is that it can hurt app performance.
  2. No support for Server-Side Rendering (SSR): Because SSR requires asynchronous rendering to avoid blocking the server thread, using useLayoutEffect in an SSR setup can result in mismatches between server-rendered and client-rendered content.

Best Practices for using useLayoutEffect

When using the useLayoutEffect hook in React, it's important to follow best practices to ensure that your code behaves correctly and efficiently. Here are some recommended practices for using useLayoutEffect effectively:

  • useLayoutEffect is a Hook, and hence must be called at the top level of your component.
  • Do not call it inside loops or conditions. If you need to do that then extract a component and move the Effect there.
  • Only use the useLayoutEffect hook for side effects that depend on the layout of the component.
  • Also make use of the ref object in order to access the current layout of the component.
  • Avoid using useLayoutEffect to update the state of your components.
  • Avoid carrying out expensive operations and computations that could significantly cause a delay in rendering.
  • Always consider using alternatives such as useEffect.
  • Limit the use of useLayoutEffect: In most circumstances, useEffect will suffice to deliver the needed functionality. Use useLayoutEffect only in situations when synchronous execution and quick access to the DOM is required.
  • Be mindful of dependencies: Just like useEffect, useLayoutEffect hook also accepts an array of dependencies as the second argument. So ensure you include all the relevant dependencies to avoid unnecessary re-renders.

Choosing the Right Hook

There is no right or wrong hook to use it all depends on your specific use case. So I would recommend starting with theuseeffect hook and switching over when it causes a problem.

Conclusion

Throughout this article, you have learnt a lot about useLayoutEffect hook, similarities and differences, best practices etc. By now I am confident you know enough to properly make use of effect hooks in your applications to improve the overall experience and solve great problems.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .