title: In React, fetch data conditional on results of an initial fetch
body: <p>We have written a custom data fetching hook <code>useInternalApi</code> which is similar to the <code>useDataApi</code> hook at the very bottom of this <a href="https://www.robinwieruch.de/react-hooks-fetch-data" rel="nofollow noreferrer">fairly decent tutorial</a> on data fetching with react hooks. Our app fetches a lot of sports data, and in particular, we are trying to figure out the right data-fetching pattern for our use case, which is fairly simple:</p>
  <ul>
  <li>Fetch general info for a specific entity (an NCAA conference, for example)</li>
  <li>Use info returned with that entity (an array of team IDs for teams in the specific conference), and fetch info on each team in the array.</li>
  </ul>
  <p>For this, our code would then look something like this:</p>
  <pre><code>import `useInternalApi` from '../path-to-hooks/useInternalApi';
  // import React... and other stuff
  
  function ComponentThatWantsTeamInfo({ conferenceId }) {
      // use data fetching hook
      const [conferenceInfo, isLoading1, isError1] = useInternalApi('conferenceInfo', { conferenceId: conferenceId })
  
      // once conferenceInfo loads, then load info from all teams in the conference
      if (conferenceInfo &amp;&amp; conferenceInfo.teamsArray) {
          const [teamInfos, isLoading2, isError2] = useInternalApi('teamInfo', { teamIds: conferenceInfo.teamIds })
      }
  }
  </code></pre>
  <p>In the example above, <code>conferenceId</code> is an integer, <code>teamIds</code> is an array of integers, and the combination of the 2 parameters to the <code>useInternalApi</code> function create a unique endpoint url to fetch data from. The two main problems with this currently are:</p>
  <ol>
  <li>Our <code>useInternalApi</code> hook is called in an <code>if</code> statement, which is not allowed per #1 rule of hooks.</li>
  <li><code>useInternalApi</code> is currently built to only make a single fetch, to a specific endpoint. Currently, it cannot handle an array of teamIds like above.</li>
  </ol>
  <p>What is the correct data-fetching pattern for this? Ideally, <code>teamInfos</code> would be an object where each key is the <code>teamId</code> for one of the teams in the conference. In particular, is it better to:</p>
  <ol>
  <li>Create a new internal hook that can handle an array of teamIds, will make the 10 - 20 fetches (or as many as needed based on the length of the <code>teamsArray</code>), and will use Promise.all() to return the results all-together.</li>
  <li>Keep the <code>useInternalApi</code> hook as is, and simply call it 10 - 20 times, once for each team.</li>
  </ol>
  <h2>Edit</h2>
  <p>I'm not sure if the underlying code to <code>useInternalApi</code> is needed to answer this question. I try to avoid creating very long posts, but in this instance perhaps that code is important:</p>
  <pre><code>const useInternalApi = (endpoint, config) =&gt; {
      // Set Data-Fetching State
      const [data, setData] = useState(null);
      const [isLoading, setIsLoading] = useState(true);
      const [isError, setIsError] = useState(false);
  
      // Use in lieu of useEffect
      useDeepCompareEffect(() =&gt; {
          // Token/Source should be created before &quot;fetchData&quot;
          let source = axios.CancelToken.source();
          let isMounted = true;
  
          // Create Function that makes Axios requests
          const fetchData = async () =&gt; {
              // Set States + Try To Fetch
              setIsError(false);
              setIsLoading(true);
              try {
                  const url = createUrl(endpoint, config);
                  const result = await axios.get(url, { cancelToken: source.token });
                  if (isMounted) {
                      setData(result.data);
                  }
              } catch (error) {
                  if (isMounted) {
                      setIsError(true);
                  }
              } finally {
                  if (isMounted) {
                      setIsLoading(false);
                  }
              }
          };
  
          // Call Function
          fetchData();
  
          // Cancel Request / Prevent State Updates (Memory Leaks) in cleanup function
          return () =&gt; {
              isMounted = false; // set to false to prevent state updates / memory leaks
              source.cancel(); // and cancel the http request as well because why not
          };
      }, [endpoint, config]);
  
      // Return as length-3 array
      return [data, isLoading, isError];
  };
  </code></pre>