What Is useEffect? What Is the Difference Between useEffect and useLayoutEffect?

February 15, 2023

☕️ Support Us
Your support will help us to continue to provide quality content.👉 Buy Me a Coffee

useEffect is a React Hook that is often used in React applications. It is also a common React interview question, including how to use useEffect, the execution time of useEffect, etc. Another similar Hook to useEffect is useLayoutEffect, which is also a high-frequency question in React interviews.

What is useEffect?

useEffect is a React Hook that is used to connect to external systems. React function components need to be pure functions, but if we need to perform operations with side effects, such as: request API, use third-party libraries, we need to place this code in useEffect to execute. External systems include: server-side, browser-provided APIs, or third-party libraries. Because this part is not handled by React itself, it is called an external system.

useEffect rules

Can only be called at the top level

useEffect is also a kind of hook, so it can only be called at the top level and cannot be used in loops, conditionals, or nested functions.

useEffect accepts two parameters: setup function and dependencies (optional)

  1. setup function

    The setup function contains the code for how to connect to external systems. If you need to clear the logic, you can return a cleanup function in the setup function.

  2. dependencies

    The dependencies parameter is an optional array that can pass in props, state, or any variables used in the component. React will use the Object.is algorithm to compare. If any value in dependencies is different from the previous one, the useEffect will be re-executed. (If you want to know the details of Object.is, you can read this article 《In JavaScript, ==, === and Object.is() Difference》).

import { useEffect } from "react";
import { createConnection } from "./blog.js";

function Article({ articleId }) {
  const [serverUrl, setServerUrl] = useState("https://blog.com/0");

  useEffect(() => {
    const connection = createConnection(serverUrl, articleId);
    connection.connect();

    // 回傳 cleanip function
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, articleId]);
  // ...
}

useEffect execution time

  1. When the component is added (mount), useEffect will be executed for the first time.
  2. When the component is re-rendered, if the value of dependencies has changed, the old props and state will execute the cleanup function, and the new props and state will execute the setup function.
  3. The cleanup function code will be executed for the last time when the component lifecycle ends (unmount).

To prevent flicker when using useEffect, how to solve?

Solve: Try to replace useEffect with useLayoutEffect.

In the following section, we will discuss a similar Hook to useEffect - useLayoutEffect.

As the title suggests, useLayoutEffect is typically used to handle situations where useEffect causes flicker on the screen, the detailed reason will be mentioned below.

What is useLayoutEffect?

useLayoutEffect is a React Hook that is similar to useEffect. The parameters passed in are the same, but the execution time is different. It will be executed before the browser repaints.

useLayoutEffect may cause performance issues because the code in useLayoutEffect will block the browser from repainting, which may cause the entire application to slow down. Therefore, it is generally recommended to use useEffect first, and only use useLayoutEffect if the problem cannot be solved.

useEffect and useLayoutEffect comparison

The following provides a code example, which can clearly feel the difference between the two. (Note: the following code example is written to highlight the difference between the two, actual development will not be written like this)

import { useEffect, useLayoutEffect, useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    if (count === 0) {
      const randomNum = 1 + Math.random() * 1000;
      setCount(randomNum);
    }
  }, [count]);

  return <div onClick={() => setCount(0)}>{count}</div>;
}

When we execute the above code, click the div block continuously, we will see the screen flashing.

The reason is that when you click the div, the count will be updated to 0, and the screen will be re-rendered to 0. At the same time, because the count is updated, useEffect will also be triggered. So after the re-painting is completed, useEffect is executed and the count is updated to another random number, and the screen will be re-rendered again. Because the two re-renders are very fast, the screen flashes.

What is the difference between useEffect and useLayoutEffect?

If we replace the useEffect in the above code with useLayoutEffect, when you click the div, the count will be updated to 0, but the screen will not be re-rendered to 0. Instead, it will wait for the code in useLayoutEffect to be executed, and the state has been updated to a new random number, and then the screen will be re-painted.

Followup questions

If you don't specify dependencies, when will useEffect be executed?

If you don't specify dependencies, the useEffect will be executed every time the component is re-rendered.

If an object is included in the dependencies array, what will happen?

The "options" object in the code below will be a different object every time the component re-renders, so this useEffect might run on every render because the options in the dependencies have changed.

function ChatRoom({ articleId }) {
  const [article, setArticle] = useState(null);

	// options will be a different object every time the component re-renders
  const options = {
    serverUrl: 'https://localhost:1234',
    articleId: articleId
  };

  useEffect(() => {
    const data = getArticle(options);
    setArticle(data)

  // options will be a different object every time the component re-renders
  }, [options]);

If you want to avoid unnecessary triggering of useEffect in the above code, you can actually put the dynamic object in the Effect, and change the object in the dependencies to a string or number, as shown below.

function ChatRoom({ articleId }) {
  const [article, setArticle] = useState(null);

  useEffect(() => {
	  const options = {
	    serverUrl: 'https://localhost:1234',
	    articleId: articleId
	  };

    const data = getArticle(options);
    setArticle(data)

  }, [articleId]);

When to use useLayoutEffect?

As mentioned above, useLayoutEffect is typically used to handle situations where useEffect causes flicker on the screen.

As mentioned above, frequent use of useLayoutEffect can harm the performance of an application and it's only recommended to be used in certain circumstances. Below is an example of such a scenario (this example is from React Docs Beta).

Suppose we are designing a tooltip component that appears above, below or beside an element based on certain conditions. This design condition means that we need to know the exact height position of the element to determine where to show the tooltip.

React's rendering process can be divided into the following steps:

Render the Tooltip anywhere (even if the position is incorrect) Measure the height of the element and decide the position of the Tooltip Re-render the screen, this time the Tooltip's position will be correct If using useEffect, the Tooltip's position may go from 0 to 10, causing screen flicker and poor user experience. If using useLayoutEffect, React will recalculate the correct position before re-rendering the screen.

The code can be referenced at this link React Docs Beta.

☕️ Support Us
Your support will help us to continue to provide quality content.👉 Buy Me a Coffee