javascriptreactjsfetch-apipromise.all

How to do multiple fetch requests


I would like to do multiple fetch requests with React/JavaScript, I have the following attempt which kind of works:

const fetchAll = async () => {
    Promise.all([
        await fetch('/api/...'),
        await fetch('/api/...')
    ]).then(links => {
        const response1 = links[0];
        const response2 = links[1];

        timeData = response1.json();
        functionData = response2.json();
    })
}

But I would like to do it this way, because that seems more useful. I would like to use useEffect and useState and load the data of the different APIs in different arrays in useState if that's possible. Here is an example:

const [data, setData] = useState([]);
useEffect(() => {
        fetch("/api/..")
            .then((response) => response.json())
            .then((r) => {
                setData(r);
            });
    }, []);

Is there a way to do this for multiple requests and save the data in different arrays so I can access them later on?


Solution

  • This can be done nicely by

    1. Creating an array of URLs to resolve
    2. Iterating the returned array data
    3. Passing that data into your setters

    For example, create some helper functions in some module

    // fetch-helpers.js
    
    // performs a request and resolves with JSON
    export const fetchJson = async (url, init = {}) => {
      const res = await fetch(url, init);
      if (!res.ok) {
        throw new Error(`${res.status}: ${await res.text()}`);
      }
      return res.json();
    };
    
    // get JSON from multiple URLs and pass to setters
    export const fetchAndSetAll = async (collection) => {
      // fetch all data first
      const allData = await Promise.all(
        collection.map(({ url, init }) => fetchJson(url, init))
      );
    
      // iterate setters and pass in data
      collection.forEach(({ setter }, i) => {
        setter(allData[i]);
      });
    };
    

    and in your component...

    import { useEffect, useState } from "react";
    import { fetchAndSetAll } from "./fetch-helpers";
    
    export const MyComponent = () => {
      // initialise state to match the API response data types
      const [timeData, setTimeData] = useState([]);
      const [functionData, setFunctionData] = useState([]);
    
      useEffect(() => {
        fetchAndSetAll([
          {
            url: "/api/...",
            setter: setTimeData,
          },
          {
            url: "/api/...",
            setter: setFunctionData,
          },
        ]).catch(console.error);
      }, []);
    
      return <>{/* ... */}</>;
    };