What Is useEffect? What Is the Difference Between useEffect and useLayoutEffect?
February 15, 2023
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)
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.
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, theuseEffect
will be re-executed. (If you want to know the details ofObject.is
, you can read this article 《In JavaScript, ==, === andObject.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
- When the component is added (mount),
useEffect
will be executed for the first time. - 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.
- 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.