title: How to share asset requests between components using react hooks?
body: <p>I am looking for a good way to <strong>share a single asset request/response</strong> between any number of consumers using hooks.</p>
  
  <p>In the code snippet provided, I use the URL to keep track of the number of components that are requesting the same asset. I store the request and if component 2 makes the same request before component 1 unmounts, component 2 will <strong>use the cached request</strong>. If all components are unmounted, we can abort the request and remove it from the cache.</p>
  
  <pre><code>/*
   * Share asset requests between components.
   * const { data, loaded, total, error } = useAssetLoader('largeFile.tiff', 'arraybuffer');
   */
  
  import { useEffect, useState } from 'react';
  import request from 'superagent';
  
  // Keep track of how many instances are using a each request
  // when all instances have been unmounted, we can abort the request
  const instances = {};
  
  // Keep track of requests to share between instances
  const requests = {};
  
  export default function useAssetLoader(url, responseType) {
    const [data, setData] = useState();
    const [loaded, setLoaded] = useState();
    const [total, setTotal] = useState();
    const [error, setError] = useState();
  
    useEffect(() =&gt; {
      if (!url) {
        return () =&gt; {};
      }
  
      const key = url + responseType;
  
      instances[key] = (instances[key] || 0) + 1;
  
      if (!requests[key]) {
        requests[key] = request(url);
  
        if (responseType) {
          requests[key].responseType(responseType);
        }
      }
  
      requests[key].on('progress', (event) =&gt; {
        if (event.direction === 'download') {
          setLoaded(event.loaded);
          setTotal(event.total);
        }
      });
  
      requests[key].on('error', setError);
  
      req.then(response =&gt; setData(response.body || response.text));
  
      return () =&gt; {
        instances[key] -= 1;
  
        // When all components are unmounted we can abort request
        if (instances[key] === 0) {
          delete instances[key];
  
          if (requests[key]) {
            requests[key].abort();
            delete requests[key];
          }
        }
      };
    }, [url, responseType]);
  
    return { data, loaded, total, error };
  }
  </code></pre>
  
  <p>Is there a better way of doing this? I would like to use this pattern for other asynchronous tasks as well.</p>