1

Hello guys I have an array like this :

[
 {
   "name": "test",
   "amount": 794.651786,
   "id": "60477897fd230655b337a1e6"
 },
 {
   "name": "test2",
   "amount": 10.80918,
   "id": "60477bfbfd230655b337a1e9"
 }
] 

And i wan't to make the total of every amount.

I tried by using the useState hook like this :

const [total, setTotal] = useState(Number);
array.map((item) => {

 setTotal(total + item.amount);

});

but it doesn't seems to work as expected.

ogtm
  • 259
  • 1
  • 2
  • 10
  • How do you expect it to work and how does it currently work? – Dancrumb Mar 09 '21 at 16:51
  • First and foremost, I invite you to read this https://stackoverflow.com/questions/34426458/javascript-difference-between-foreach-and-map – jperl Mar 09 '21 at 16:56
  • What is `array` - in `array.map` - is it a prop? Is it some other state? The reason is, it's likely you don't want to use `state` here at all. To clarify further, if `array` is some other state or some other prop, then `total` isn't state, it's `computed state` and you just want `useMemo` (or nothing at all). – Adam Jenkins Mar 09 '21 at 16:58

4 Answers4

5

You could use the reduce method, see docs.

setTotal(array.reduce((sum, item) => sum + item.amount, 0))

I invite you to read this JavaScript: Difference between .forEach() and .map() as well. You should never use .map like this. For this use case, use .forEach instead.

jperl
  • 4,662
  • 2
  • 16
  • 29
  • 2
    This is far better than calling `setState` in a loop, but I still don't think the OP should be using state here at all. – Adam Jenkins Mar 09 '21 at 17:00
3

You would want to update the state with the minimum calls needed. so first, I would do it like this:

let _total = 0;
array.forEach((item) => {
   _total += item.amount;
});
setTotal(_total);

That said, You would want to only execute this if array has changed. Assuming array is a prop, this can be done easily with useEffect hook:

useEffect(()=>{
    let _total = 0;
    array.forEach((item) => {
        _total += item.amount;
    });
setTotal(_total);
},[array]);

Hope this helps you get a full picture of what the best practice would be. Also you can check out the rules of hooks to get a better understanding on where is best to call setState

Fabio Lopez
  • 517
  • 2
  • 5
  • 15
2

My comment wasn't addressed but I'm going to add an answer which addresses my concern - total shouldn't be state at all.

total most likely isn't state - it's computed state - i.e. it's derived from other state and/or props.

If that's the case (99% that it is) it's not correct to set total as state, that just makes for more code and more complicated debugging:

Examples:

When the source of data is a prop:

const Cart = ({someItemsInMyCart}) => {
   const total = useMemo(() => someItemsInMyCart.reduce((acc,item) => acc+item.amount,0),[someItemsInMyCart]);

   return (/* some JSX */);
}

When the source of data is state:

const Cart = () => {
   const [items,setItems] = useState([]);
   const total = useMemo(() => items.reduce((acc,item) => acc+item.amount,0),[items]);

   return (/* some JSX */);
}

You can write those two examples above and completely leave out the useMemo, which is just a perf optimization, because reducing an array in that manner is pretty darn fast unless you're dealing with 1000s of items.

Adam Jenkins
  • 51,445
  • 11
  • 72
  • 100
0

Try this way

const [total, setTotal] = useState(0);

array.map((item) => {
 setTotal(prevCount => prevCount + item.amount);    
});
Surya
  • 546
  • 7
  • 13