Reputation: 67
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
Reputation: 164731
This can be done nicely by
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
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
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 fetch
es; 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