title: Best practice to prevent state update warning for unmounted component from a handler
body: <p>It is a common use-case to fetch and display the data from an external API (by using XHR requests) when a certain UI component (e.g. a <code>&lt;button /&gt;</code>) is clicked. However, if the component was unmounted in the meantime, the following warning appears in the console:</p>
  
  <blockquote>
    <p>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.</p>
  </blockquote>
  
  <p>In fact, the most common solution (<a href="https://twitter.com/housecor/status/1093142219165499392" rel="nofollow noreferrer">approved by @dan-abramov</a>) to avoid the warning seems to keep track of the mount state of the component by using the return function of  <code>useEffect</code> to cleanup.</p>
  
  <pre class="lang-js prettyprint-override"><code>import React, { useState, useRef, useEffect } from "react";
  import axios from "axios";
  
  export default function PhotoList() {
    const mounted = useRef(true);
    const [photos, setPhotos] = useState(null);
  
    useEffect(() =&gt; {
      return () =&gt; {
        mounted.current = false;
      };
    }, []);
  
    function handleLoadPhotos() {
      axios("https://jsonplaceholder.typicode.com/photos").then(res =&gt; {
        if (mounted.current) {
          setPhotos(res.data);
        }
      });
    }
  
    return (
      &lt;div&gt;
        &lt;button onClick={handleLoadPhotos}&gt;Load photos&lt;/button&gt;
        {photos &amp;&amp; &lt;p&gt;Loaded {photos.length} photos&lt;/p&gt;}
      &lt;/div&gt;
    );
  }
  </code></pre>
  
  <p>However, this seems to cause unnecessary overhead to keep track of the mounting state and to check it before every state update. This becomes especially obvious when Observables (where you can <a href="https://rxjs-dev.firebaseapp.com/guide/subscription" rel="nofollow noreferrer">unsubscribe</a>) instead of Promises are used.</p>
  
  <p>While you indeed can unsubscribe inside of the <code>useEffect</code> using the cleanup function in a very neat way:</p>
  
  <pre class="lang-js prettyprint-override"><code>useEffect(() =&gt; {
    // getPhotos() returns an observable of the photo list
    const photos$ = getPhotos().subscribe(setPhotos);
    return () =&gt; photos$.unsubscribe();
  }, []);
  </code></pre>
  
  <p>The same smart cleanup is not possible within a handler:</p>
  
  <pre class="lang-js prettyprint-override"><code>function handleLoadPhotos() {
    const photos$ = getPhotos().subscribe(setPhotos);
    // how to unsubscribe on unmounting?
  }
  </code></pre>
  
  <p>Is there a best practice to avoid the warning without the ugly manual tracking of the mounting state with <code>useRef()</code>? Are there good approaches for that when using Observables?</p>