0

I have this code:

let [totalPromos, setTotalPromos] = useState(0);

for (let item of cart.items){
    let response = await api.get('/products/' + item.id);
    let price = response.data.price;
    let promotion = response.data.promotions[0];
    setTotalPromos(totalPromos + 1);
}

I have 2 items in cart, that I call an api with axios to reach its promotion. I am not able to sum the total of promos of my products. The value is getting only the final value like in this code, which is 1. What I am doing wrong?

Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • 4
    Does this answer your question? [try/catch blocks with async/await](https://stackoverflow.com/questions/40884153/try-catch-blocks-with-async-await) – yudhiesh Jan 27 '21 at 12:19

2 Answers2

0

I think you should use this code like.

let [totalPromos, setTotalPromos] = useState(0);

for (let item of cart.items){
    api.get('/products/' + item.id)
    .then((response) => {
      if (response) {
        let price = response.data.price;
        let promotion = response.data.promotions[0];
        setTotalPromos(totalPromos + 1);
        return;
      }
      console.log("Not a valid response");
    })
    .catch((error) => {  // Failed to resolve then go to chach block
      console.log(error);
    }
}

OR you can use

async function getProductById() {
  try {
    var response = await api.get('/products/' + item.id);
     if (response) {
        let price = response.data.price;
        let promotion = response.data.promotions[0];
        setTotalPromos(totalPromos + 1);
        return;
      }
      console.log("Not a valid response");
  } catch (error) {
    console.error(error);
  }
}

You can use which one you want but I personally prefer the first one.

Nitish
  • 995
  • 7
  • 17
0

Move the async calls to useEffect(). Use the Functional updates option for setting the state, so the useEffect() won't depend on totalPromos, because it will restart on each render (the render is caused by setting the state).

<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

<script src="https://unpkg.com/@babel/standalone@7.12.12/babel.min.js"></script>


<div id="root"></div>

<script type="text/jsx">

  const { useState, useEffect } = React;

  const Demo = ({ items }) => {
    const [totalPromos, setTotalPromos] = useState(0);

    useEffect(async() => {
      for (const item of items){
        const price = await get(item);
        setTotalPromos(totalPromos => totalPromos + price);
      }
    }, [items]);

    return (
      <div>
        {totalPromos}
      </div>
    );
  }

  const items = [1, 2, 3, 4];

  ReactDOM.render(
    <Demo items={items} />,
    root
  );

  // mock async get
  function get(item) {
    return new Promise(resolve => setTimeout(() => resolve(item), 100));
  }

</script>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • Man, I´m really thankful! Setting setTotalPromos(totalPromos => totalPromos + price) worked! I just didn´t understand why do I need to set using "=>"? – Marcos Quixadá Jan 27 '21 at 14:25
  • If anything in the `useEffects()` dependancy list is change (and `totalPromos` will change every time), the function would be called again. To prevent that you can use Functional updates. Instead of getting the value, the set state receives that will create the new state. The function is called with the current value (`totalPromos`), and you don't have to pass it explicitly. I've added a link in the answer. – Ori Drori Jan 27 '21 at 14:33