salteax1
salteax1

Reputation: 67

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?

Upvotes: 5

Views: 14647

Answers (3)

Phil
Phil

Reputation: 164731

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 <>{/* ... */}</>;
};

Upvotes: 2

Manishyadav
Manishyadav

Reputation: 1728

Try this,

As of today, fetch is now implemented in all the latest version of the major browsers, with the exception of IE11, a wrapper could still be useful unless you use a polyfill for it.

Then, taking advantage of newer and now more stable javascript features like destructuring and async/await, you might be able to use a similar solution to the same problem (see the code below).

I believe that even though at first sight may seem a little more code, is actually a cleaner approach. Hope it helps.

try {
  let [items, contactlist, itemgroup] = await Promise.all([
    fetch("http://localhost:3000/items/get"),
    fetch("http://localhost:3000/contactlist/get"),
    fetch("http://localhost:3000/itemgroup/get")
  ]);

  ReactDOM.render(
    <Test items={items} contactlist={contactlist} itemgroup={itemgroup} />,
      document.getElementById('overview');
  );
}
catch(err) {
  console.log(err);
};

Upvotes: 2

Alexander Nied
Alexander Nied

Reputation: 13623

If I'm understanding your question correctly, then I think you want something like this:

const [resp1, setResp1] = useState();
const [resp2, setResp2] = useState();

useEffect(() => {
    Promise.all([
        fetch('/api/...'),
        fetch('/api/...')
    ]).then(links => {
        const response1 = links[0];
        const response2 = links[1];
        
        setResp1(response1);
        setResp2(response2);
    })
}, [/*dependency array*/])

Here we leverage a useEffect to make your simultaneous fetches; we don't really need nor want to use await because we want them to run simultaneously and the Promise.all will only resolve when all the promises in the array resolve. In the .then of the Promse.all, you can parse out the responses just as you did in your original example and set them in state hooks you've defined earlier in your component. You'd probably want to make sure that you have an appropriately-defined dependency array passed as a second arg to your useEffect, in order to make sure that it only executes under the conditions in which you want it to. This would work for some limited number of fetches-- for something dynamic or with very large numbers of fetches, this approach would probably be less ideal, and you'd want something that scaled better.

Upvotes: 3

Related Questions