1. Translator’s Preface

After the recent release of React 18, some changes affected our use of React Hooks. Update to remove the “setState on unmounted Component “warning Update to remove the “setState on unmounted Component” warning This is the first in a series on the effects of React 18 on Hooks. I will be sorting out other important changes to keep from getting lost.

2. Translation

2.1 background

We have removed the warning that was given when setState was called in an unloaded component. The warning reads as follows:

Warning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

Warning: State cannot be changed in components that have been unloaded. This is a useless operation that indicates that there is a memory leak in your project. To resolve this problem, unsubscribe all subscriptions and asynchronous tasks in the useEffect cleanup function.

Unfortunately, this warning is often misunderstood and misleading.

It was intended to ensure that the following example would work:

useEffect(() = > {
  function handleChange() {
     setState(store.getState())
  }
  store.subscribe(handleChange)
  return () = > store.unsubscribe(handleChange)
}, [])
Copy the code

In this case, if you forgot to call unsubscribe in the effect cleanup function, there must be a memory leak.

2.2 Why is this warning misleading?

In fact, the above scenario is not common. Instead, the following scenario is more common:

async function handleSubmit() {
  setPending(true)
  await post('/someapi') // component might unmount while we're waiting
  setPending(false)}Copy the code

In the above code, a warning is thrown if the component is unloaded when the request is sent. But in this case, the warning is misleading.

There is no memory leak:

  • The Promise completes execution quickly, and the memory is reclaimed by the garbage collection mechanism
  • Even if the execution doesn’t complete quickly, this warning is useless, because garbage collection still has to wait for the Promise to complete the collection, and you can’t do anything

In general, we eliminate warnings with code like this:

let isMountedRef = useRef(false)
useEffect(() = > {
  isMountedRef.current = true
  return () = > {
    isMountedRef.current = false}}, [])async function handleSubmit() {
  setPending(true)
  await post('/someapi')
  if(! isMountedRef.current) { setPending(false)}}Copy the code

In fact, this notation is useless and does not solve the so-called “memory leak”, it merely suppresses the warning. As mentioned earlier, there are no memory leaks. Memory is freed as the Promise runs out, and nothing is executing indefinitely.

2.3 The above suppression warning scheme is worse than no processing

The solution to the above suppression warnings is now very, very common. But it doesn’t actually do any good and is worse than not dealing with it:

  • In the future, React will provide the ability to save the current state of the component when it is not visible, but it will still be unloaded. The next time the component is loaded, we will render the component with the previously saved state to restore the previous page. SetPending (false) is not executed after the component is unloaded, so pending will always be true. The next time the component is restored, it will look like the request has not completed, which will be worse. (The next article describes the detailed behavioral and impact aspects of this area.)

  • Suppose the user clicks a button, initiates a network request, and updates state when the request is complete. To avoid this warning, some people place the request behavior in useEffect, where component uninstalls can be listened for to ignore subsequent state updates and eliminate the warning. This code becomes very unclear, very bad! Because of this false warning, people write worse code.

2.4 Remove Warning

Eventually, we decided to remove the warning. This warning is intended to solve a subscription problem that is not common in everyday code. In most cases, it’s misleading to write worse code to avoid alarms. Hopefully, the removal of this warning will allow you to remove isMounted from your code.

3. Translator’s summary

UseUnmountedRef, useSafeState, etc. in Ahooks are designed to solve this warning. In order to avoid this alarm, we will also ignore subsequent setState after component uninstallation in ahooks where necessary.

For now, this code is redundant, and the scenarios will be optimized by ahooks, but not too soon. React 16/17 still generates this alarm, causing unnecessary trouble for newcomers. We will wait for React 18 to have a wider coverage before optimizing the code.

Do not need to use useUnmountedRef, useSafeState, etc.