monkeys773
monkeys773

Reputation: 83

Performing an api call on each item in an array and appending the result to a new array

I have an array and I am trying to perform an api call on each item. Like so -

shoppingData.items.map(item => {
        getItemInformation(item.id)
          .then(response => response.json())
          .then(data => {
            JSON.parse(data.text);
          });

getItemInformation is my api call -

export async function getItemInformation(id) {
  try {
    const req = await fetch(`**api url**`);
    return await req;
  } catch (e) {
    console.error(e);
    return 'Error';
  }
}

However, once I have parsed the data, I would like to append it to a new array. This new array will then be used to render a component later down the page like so -

 {newArray?.map((item, index) => (
              <ShoppingItem key={index} itemDescription={item.description} itemCatergory={item.catergory} />
            ))}

Im having issues doing this as I have been trying to do it in a useEffect as ideally I need it to happen when the page renders. Furthermore, I tried having newArray as a state e.g const [newArray, setNewArray] = useState([]) but because I am appending items to an array, setNewArray wouldn't allow me to do this.

Upvotes: 1

Views: 1757

Answers (2)

Victor Cheng
Victor Cheng

Reputation: 112

I am not sure how do you append the data to your array while you are calling async function in .map without waiting for the result.

I assume you may be trying something like this:

  const parsedData = shoppingData.items.map(item => {
            getItemInformation(item.id)
              .then(response => response.json())
              .then(data => {
                JSON.parse(data.text);
   });
   setNewArray(parsedData);

There are two points to note. Firstly getItemInformation() is called on each item async so the order of parsedItem may not be what your expected. Secondly setNewArray() is called while parsedData's async calls may not finish.

One possible way you can try, if you really want to use .map, is using Promise.all(). Promise.all() resolves when all its promises are resolved:

const getParsedData = async() => {
    return Promise.all(shoppingData.items.map(item => getItemInfo.......));
}

const data = await getParsedData();

then append data to your array.

Another simpler solution is using for-loop and await:

for (let i=0;i<shoppingData.items.length;i++){
   const parsedItem = await getItemInformation(shoppingData.items[i]).........
   newArray.push(parsedItem);
}

Upvotes: 0

Jamiec
Jamiec

Reputation: 136239

use Promise.all

const allItems = await Promise.all(
    shoppingData.items.map(
           item =>  getItemInformation(item.id)
                       .then(response => response.json())
    )
);

You could also simplify this a bit by putting all the "asyncy" stuff into your getItemInformation method

const allItems = await Promise.all(
    shoppingData.items.map(
       item =>  getItemInformation(item.id)
    )
);

and

export async function getItemInformation(id) {
  try {
    const req = await fetch(`**api url**`);
    const json = await req.json();
    return json;
  } catch (e) {
    console.error(e);
    return 'Error';
  }
}

Live example usiong JSONPlaceholder demo api:

async function getItemInformation(id) {
  console.log("getItemInformation",id);
  try {
    const req = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);
    const json = await req.json();
    return json;
  } catch (e) {
    console.error(e);
    return 'Error';
  }
}


(async function testIt() {
  const shoppingData = {
    items: [{
      id: 1
    }, {
      id: 2
    }, {
      id: 3
    }]
  };

  const allItems = await Promise.all(
    shoppingData.items.map(
      item => getItemInformation(item.id)
    )

  );
  console.log(allItems);
})()

You can easily call setNewArray(allItems) in react useEffect using this code (basically where I did consolew.log(allItems) above.

Upvotes: 1

Related Questions