tamasys
tamasys

Reputation: 59

Fetch from two APIs in loop and combine

I'm trying to do what I thought would be pretty straightforward, but is turning out to be pretty hard. Essentially what I want to do is when loading the page, loop through an array of IDs, make a call to two APIs for each ID, merge the responses together for each ID, and save that to a React state variable itemList.

This is the code I have so far. The two getInfo() and getOtherInfo() functions are doing a basic JavaScript Fetch and are returning a Promise and then the decoded JSON data asynchronously no problem. I can even display the returned data on the page as it gets loaded, so that part works fine.

useEffect(() => {
  idList.forEach((id) => {
    getInfo(id)
      .then((response) => {
        getOtherInfo(id).then((otherReponse) => {
          response.otherInfo = otherResponse;
        });
        setItemList((itemList) => [...itemList, response]);
      })
      .catch((error) => console.log("Loading failed: " + error));
  });
}, []);

I know that the nested .then() is wrong, as it's trying to add response data to a potentially unresolved Promise, but it's my first time using React Hooks and the Fetch API so I'm not sure what the right way to do it is. I've found a couple other similar questions, but they don't seem to deal with joining the data together after it gets returned from the two APIs.

Can someone point me in the right direction here?

Upvotes: 1

Views: 2508

Answers (2)

guhou
guhou

Reputation: 1732

I know that the nested .then() is wrong, as it's trying to add response data to a potentially unresolved Promise

If you need the resolved values of response and otherResponse before continuing, it's better to put the logic needed inside the inner then() continuation. This is what you've done when setting response.otherInfo = otherResponse, but I think the setItemList call should also be inside that continuation.

useEffect(() => {
  idList.forEach((id) => {
    getInfo(id)
      .then((response) => {
        getOtherInfo(id).then((otherReponse) => {
          response.otherInfo = otherResponse;
          setItemList((itemList) => [...itemList, response]);
        });
      })
      .catch((error) => console.log("Loading failed: " + error));
  });
}, []);

If the request to getOtherInfo is not dependent on the outcome of getInfo, then it may be beneficial to execute those requests concurrently using Promise.all.

useEffect(() => {
  idList.forEach((id) => {
    Promise.all([getInfo(id), getOtherInfo(id)])
      .then(([response, otherResponse]) => {
        response.otherInfo = otherResponse;
        setItemList((itemList) => [...itemList, response]);
      })
      .catch((error) => console.log("Loading failed: " + error));
  });
}, []);

Upvotes: 1

Jain
Jain

Reputation: 1249

Try this

var promiseArr = [];    
idList.forEach((id) => {
  promiseArr.push(getInfo(id));
    promiseArr.push(getOtherInfo(id));
});

Promise.all(promiseArr).then((resp) => {
   console.log(resp);
}).catch((error) => console.log("Loading failed: " + error));

Upvotes: 1

Related Questions