Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I check if the component is unmounted in a functional component?

Tags:

reactjs

A callback function sets the component state. But sometimes subscription which supplies the data need to end. Because callback is executed asynchronously, it's not aware if the subscription ends just after making the service call (which executes the callback function).

Then I see following error in the console:

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.

Is there a way to access the component state, even if I am in the callback function?

This would be the steps:

  • subscribe with parameters
  • unsubscribe
  • component is unmounted
  • subscribed service executes the callback function
  • callback functio sets state in an unmounted component and it gives error above
like image 411
brainmassage Avatar asked Aug 23 '19 10:08

brainmassage


People also ask

When component will unmount is called?

ReactJS – componentWillUnmount() Method This method is called during the unmounting phase of the React Lifecycle, i.e., before the component is destroyed or unmounted from the DOM tree. This method is majorly used to cancel all the subscriptions that were previously created in the componentWillMount method.

How do you check if a component is rendered React?

Using React DevTools to highlight what components rerendered To enable it, go to "Profiler" >> click the "Cog wheel" on the right side of the top bar >> "General" tab >> Check the "Highlight updates when components render." checkbox.

How do you stop components from unmounting?

Using react-router you can easily prevent route change(which will prevent component unmount) by using Prompt . You need to manually pass the getUserConfirmation prop which is a function. You can modify this function as you like in any Router(Browser, Memory or Hash) to create your custom confirmation dialog(eg.


4 Answers

You can use a ref like this:

const mounted = useRef(false);

useEffect(() => {
    mounted.current = true;

    return () => { mounted.current = false; };
}, []);

Then in your callback you can check if mounted.current === false and avoid setting the state

like image 154
Mohamed Ramrami Avatar answered Oct 13 '22 00:10

Mohamed Ramrami


Here is some pseudo code how you can use useEffect to see if a component is mounted.

It uses useEffect to listen to someService when it receives a message it checks if the component is mounted (cleanup function is also called when component unmounts) and if it is it uses setServiceMessage that was created by useState to set messages received by the service:

import { useState, useEffect } from 'react';
import someService from 'some-service';

export default props => {
  const userId = props.userId;
  const [serviceMessage, setServiceMessage] = useState([]);
  useEffect(
    () => {
      const mounted = { current: true };
      someService.listen(
        //listen to messages for this user
        userId, 
        //callback when message is received
        message => {
          //only set message when component is mounted
          if (mounted.current) {
            setServiceMessage(serviceMessage.concat(message));
          }
      });
      //returning cleanup function
      return () => {
        //do not listen to the service anymore
        someService.stopListen(userId);
        //set mounted to false if userId changed then mounted
        //  will immediately be set to true again and someService
        //  will listen to another user's messages but if the 
        //  component is unmounted then mounted.current will 
        //  continue to be false
        mounted.current = false;
      };
    },//<-- the function passed to useEffects
    //the function passed to useEffect will be called
    //every time props.userId changes, you can pass multiple
    //values here like [userId,otherValue] and then the function
    //will be called whenever one of the values changes
    //note that when this is an object then {hi:1} is not {hi:1}
    //referential equality is checked so create this with memoization
    //if this is an object created by mapStateToProps or a function
    [userId]
  );
};
like image 26
HMR Avatar answered Oct 12 '22 22:10

HMR


This hook (insired from Mohamed answer) solves the problem in a more elegant maner:

function useMounted() {
  const mounted = useMemo(() => ({ current: true }), []);
  useEffect(() => {
    return () => { mounted.current = false}
  }, [mounted]);
  return mounted;
}

(Updated to use useMemo instead of useRef for readability).

like image 22
Matthieu G Avatar answered Oct 12 '22 23:10

Matthieu G


You can return a function from useEffect, which will be fired when a functional component unmount. Please read this

import React, { useEffect } from 'react';
const ComponentExample = () => {
    useEffect(() => {
        // Anything in here is fired on component mount.
        return () => {
            // Anything in here is fired on component unmount.
        }
    }, [])
}
like image 29
Adam Anjum Avatar answered Oct 13 '22 00:10

Adam Anjum