React Hook - Clean Up useEffect
Tags: react, hook, javascript, useeffect
Published: 2019-12-18 16:58:56

From: https://wareboss.com/react-hook-clean-up-useeffect/


<p>Last article we learn about Asynchronous actions on useEffect and our correct usage (<a rel="noreferrer noopener" aria-label="React Hook - Async funcion in useEffect (opens in a new tab)" href="https://wareboss.com/react-hook-async-function-in-useeffect/" target="_blank">React Hook - Async funcion in useEffect</a>).</p>



<p>Today we will see how to use the clean up function of <strong>useEffect </strong>React Hook.</p>



<p>When you update a state from an unmounted component, React will throw this error:</p>



<p>"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 the <strong>componentWillUnmount </strong>method."</p>


```javascript
useEffect(() => {
    //Do all the job, for example, subscribe a websocket channel
    return function(){
        //Unsubscribe websocket channel
    };
}, []);
```



<hr class="wp-block-separator"/>



<div class="wp-block-group"><div class="wp-block-group__inner-container">
<h4>Prevent update on unmounted component:</h4>



<p>Here you'll learn how to prevent this problem</p>


```javascript
  useEffect(() => {
    let isCancelled = false;
    const runAsync = async () => {
      try {
        if (!isCancelled) {
          // do the job
        }
      } catch (e) {
        if (!isCancelled) {
          throw e;
        }
      }
    };

    runAsync();

    return () => {
      isCancelled = true;
    };
  }, [...]);
```



<hr class="wp-block-separator"/>
</div></div>



<div class="wp-block-group"><div class="wp-block-group__inner-container">
<h4>Usage with setInterval/setTimeout:</h4>



<p>This is a nice solution to abort <strong>setInterval/setTimeout</strong>:</p>


```javascript
useEffect(() => {
  const interval = setInterval(() => {
    console.log('Five Seconds!');
  }, 5000);
  return () => clearInterval(interval);
}, []);
```



<p>Imagine this use case: you opened this component and then closed.<br>Withou clean up you will continues running the setInterval callback.</p>



<hr class="wp-block-separator"/>
</div></div>



<div class="wp-block-group"><div class="wp-block-group__inner-container">
<h4>Usage with Firestore Realtime Database:</h4>



<p>This is useful when using <strong>Firestore Realtime Database</strong>:</p>


```javascript
useEffect(() => {
    //Subscribe: firebase channel
    const cleanUp = firebase.firestore().collection('photos') .doc(id)
        .onSnapshot( doc => {
            setLoading(false);
            setPhotos(doc)
        }, err => { setError(err); }
    );
    return () => cleanUp(); //Unsubscribe
 }, []);
```



<p>If you forgot to clean your firestore subscription, you may receive unnecessary requests.</p>



<hr class="wp-block-separator"/>
</div></div>



<div class="wp-block-group"><div class="wp-block-group__inner-container">
<h4>Usage with fetch + AbortController:</h4>



<p>Cancel fecth with <strong>AbortController </strong>from <strong>fetch/es6</strong>:</p>


<!-- wp:enlighter/codeblock {"language":"js","highlight":"2, 5, 10, 20"} -->
```javascript
  useEffect(() => {
    const abortController = new AbortController();
    const fetchData = async () => {
      try {
        const ret = await fetch("/companies", { signal: abortController.signal });
        const data = await ret.json();
        // ...
      }
      catch(error) {
        if (abortController.signal.aborted) {
          // cancelled
        }
        else
          throw error;
      };
    };

    fetchData();

    return () => abortController.abort();
  }, [companies]);
```



<hr class="wp-block-separator"/>
</div></div>



<div class="wp-block-group"><div class="wp-block-group__inner-container">
<h4>Usage with axios request:</h4>



<p>Here is how to cancel the request with axios.</p>


```javascript
useEffect(() => {
  const source = axios.CancelToken.source();
 
  const fetchData = async () => {
    try {
      const response = await Axios.get("/companies", {
        cancelToken: source.token
      });
      // ...
    } catch (error) {
      if (Axios.isCancel(error)) {
        //cancelled
      } else {
        throw error;
      }
    }
  };

  fetchData()
 
  return () => {
    source.cancel();
  };
}, [companies]);
```


<hr class="wp-block-separator"/>


<p>The <strong>useEffect </strong>function is the equivalent of <strong>componentDidMount </strong>in a React Class component.</p>



<p>The <strong>clean up</strong> function is the equivalent of <strong>componentWillUnmount </strong>in a React Class component. </p>



<p>Next article:  <strong>React Class Component</strong> and <strong>React Function Component</strong>  differences.</p>



<p>Bye bye!</p>