Welcome to my blog

React Hooks Best Practices

useEffect

UseEffect is one of the most commonly used Hooks after useState. It can be used to manage side effects, to replace the componentDidMount and componentWillUnmount methods in traditional class components, or to execute code according to dependencies.

UseEffect is not complicated to use, but if you are not familiar with its execution and mechanism, you can easily encounter problems such as dead loops and dependency errors. Also, every time it runs, it stores a snapshot of the current state, which means that setTimeout in it does not get the latest state, but the runtime state. It is recommended to read the complete useEffect guide for further information.

This article is the second in a series of best practices for React Hooks technologies. The first article introduces useState best practices, and those interested can read Best practices for React Hooks technologies, part 1.

Alternative componentDidMount

UseEffect replaces componentDidMount by setting the second dependency parameter to [], although their timing is not exactly uniform. This allows us to useEffect operations or asynchronous requests that need to be done after the component is mounted, and it gets state. Because the dependency is [], it only runs at mount time throughout the component’s life cycle.

useEffect((a)= > {
  // Handle transactions or side effects after the component is mounted
  // State can be obtained
}, []) 
Copy the code

Side effects

Let’s see how to use async methods correctly.

Wrong example

useEffect(async() = > {const { data, result } = await asyncRequest();  
}, [])
Copy the code

UseEffect does not recommend that you use async directly in callback functions, such use will trigger an error, usually if the relevant Lint is installed. This would cause the callback functions to compete with each other, whereas Effect callbacks should be synchronized.

Recommended practices

const requestFn = async() = > {const { data, result } = await asyncRequest(); 
}

useEffect((a)= > {
  requestFn();
}, [])
Copy the code

If you need to update state after retrieving asynchronous data, you can also do this in the requestFn function.

const [value, updateValue] = useState();

const requestFn = async() = > {const { data, result } = await asyncRequest();
  updateValue(value);
}

useEffect((a)= > {
  requestFn();
}, [])
Copy the code

If the asynchronous request depends on any state, it needs to be clearly written in the useEffect dependency so that the asynchronous request will be executed each time the state changes.

const requestFn = async (param) => {
  const { data, result } = await asyncRequest(param);
  updateValue(value);
}

useEffect((a)= > {
  requestFn(param);
}, [param])
Copy the code

Rely on

Proper use

We know that every change in state or props causes the component to re-render, so useEffect is executed every time without any dependencies. If you change the state in it, it will cause an endless loop. The process is that executing useEffect changes state, which in turn causes useEffect to be repeated.

Wrong way to write it

const [count, updateCount] = useState(0);

useEffect((a)= > {
  updateCount(prevCount= > prevCount++);   
})
Copy the code

The correct way to write it is number one

const [count, updateCount] = useState(0);

// Add a precondition and update the status only when it is met
useEffect((a)= > {
  if (count < 1) {
    updateCount(prevCount= >prevCount++); }})Copy the code

The correct way to write it is two

const [count, updateCount] = useState(0);
const [num, updateNum] = useState(1)

// Effect is executed every time num changes
useEffect((a)= > {
  updateCount(prevCount= > prevCount+num);  
}, [num])
Copy the code

Depend on the function

In addition to using state and props as dependencies, functions can also be used directly as dependencies. However, since the function is re-executed each time the component renders, Effect is unnecessarily repeated.

However, you can use the useCallback method to avoid this situation.

Effect is repeated for each rendering

const doSomething = (a)= > {}

useEffect((a)= > {
  // do somethings 
}, [doSomething])
Copy the code

Using useCallback

const doSomething = (a)= > useCallback((a)= > {}, [])

useEffect((a)= > {
  // do somethings once
}, [doSomething])
Copy the code

If the function is dependent on any other state, you can add the dependency to the useCallback dependency array so that the function is re-executed if the dependency changes. Effect is also executed because it depends on the function.

To optimize the

It is important to use an Effect dependency correctly. After each dependency change, the object. is method is used to compare the dependency, and if it is different, Effect is executed. Therefore, we can optimize it by Memoization technology, specifically, each state change will be compared with the state of the previous memory, if the value is changed, the new state will be returned and the memory, if the value is not changed, only the reference is changed, the state of the memory will be returned. This Effect is executed only after the value changes.

This part was introduced in the previous article, so I won’t elaborate on it here.

Application of Memoization technology in React

useLayoutEffect

Most of the time useEffect will do, but when you need to manipulate the DOM in useEffect, use useEffect to optimize the rendering. It is executed after the DOM update is complete, and the DOM layout can be read and rendered synchronously before the browser can draw.

The next article covers the use and techniques of the other Hooks.

The official FAQ is actually written very good, many traps and skills are introduced, recommended reading.

Refer to the article

  1. UseEffect complete guide