React Enjoyer
React Enjoyer

Reputation: 1402

REACT JS - How to do a summation inside a map?

originally I was gonna ask how to call a function inside a map but I realize I wasn't doing anything wrong in the way I was calling my function so I just think that my code is wrong.

So I'm trying to do a summation of all the prices of a map i'm rendering sometimes those prices change due to the quantity that the person might want so I was calling:

//My function call inside the map
{calculateTotal(fixedPrice)}

//The function
const calculateTotal = (price) => {
        setTotalPrice (totalPrice + price)
      }

But I got this error in return

enter image description here

I don't understand how adding 2 numbers certain amount of times (in this case 9) would end up in an infinite loop. All I want to do is to sum all the prices into one variable and if the cantidad/quantity changes it add those changes too.

enter image description here

whole code in case you need it

import React, { useState, useEffect } from 'react'
import { auth, db } from './firebase';
import { useHistory } from 'react-router-dom';
import { Checkbox } from '@material-ui/core';

function CrearPedidos({user}) {
    const [libros, setLibros] = useState([]);
    const [cantidad, setCantidad] = useState( new Array(libros.length).fill(1));

    useEffect(()=>{
      setCantidad(new Array(libros.length).fill(1))
    }, [libros])

    const history = useHistory("");
    const [totalPrice, setTotalPrice] = useState();

    const librosRef = db.collection('libros');
    const queryRef = librosRef.where('grado', '==', '4° Grado');

    useEffect(() => {
        queryRef.orderBy("precio")
        .get()
        .then((snapshot) => {
              const tempData = [];
            snapshot.forEach((doc) => {

              const data = doc.data();
              tempData.push(data);
            });
            setLibros(tempData);
          });
      }, []);

      const mas = (index) => {
        setCantidad(cantidad[index]++);
        setCantidad([...cantidad])
      };

      const menos = (index) => {
        if (cantidad[index] > 0){
            setCantidad(cantidad[index]--);
            setCantidad([...cantidad])
        }
        else {
            window.alert("Sorry, Zero limit reached");
        }
      };

      const calculateTotal = (price) => {
        setTotalPrice (totalPrice + price)
      }

    return (
        <div className="listado_Pedidos"> 
        <div className="estudiantes_container">
            <h1 className = "estudiantes_container_h1">Estudiante: {user.displayName}</h1>
            <h1 className = "estudiantes_container_h1">Libros Nuevos</h1>
            <div className ="tableContainer">
            <table>
                <thead>
                    <tr className="Lista">
                        <th>Cantidad</th>
                        <th>Grado</th>
                        <th>Descripcion</th>
                        <th>Editorial</th>
                        <th>Precio</th>
                    </tr>
                </thead>
                <tbody>
                {libros.map((libros, index, fixedPrice) => (
                        <tr key={libros.id || index}>
                        <td>
                            <button onClick = {() => mas(index)}/>
                            {cantidad[index]}
                            {console.log(cantidad)}
                            <button onClick = {() => menos(index)}/>
                        </td>
                        <td>{libros.grado}</td>

                        <td >
                        <input onChange = {(event) => {
                            let checked = event.target.checked;
                        }} 
                        
                        type="checkbox" checked = "">
                        </input>
                        {libros.descripcion}
                        </td>

                        <td >{libros.editorial}</td>
                        <td >${fixedPrice = parseFloat(libros.precio).toFixed(2) * cantidad[index]}</td>
                        
                        {calculateTotal(fixedPrice)}
                        </tr>
                        
                     ))
                     }
                </tbody>
            </table>
            </div>

            <div className="space" />
            <button onClick="{realizarPedidos}" className = "crear_estudiante_boton">Realizar Pedidos</button>
            <div className="space" />
      </div>

      </div>
    )
}

export default CrearPedidos

Upvotes: 3

Views: 4603

Answers (4)

Narek Ghazaryan
Narek Ghazaryan

Reputation: 3123

Remove these lines:

const [cantidad, setCantidad] = useState(...);
const [totalPrice, setTotalPrice] = useState();

Instead use useMemo hook.

const cantidad = useMemo(() => {
  return new Array(libros.length).fill(1);
}, [libros]);

const total = useMemo(() => {
  return libros.reduce((sum, libro, index) => {
    return sum + parseFloat(libro.precio) * cantidad[index];
  }, 0);
}, [cantidad, libros]);

And then use it like:

<td>{total.tofixed(2)}</td>

Upvotes: 0

React Enjoyer
React Enjoyer

Reputation: 1402

Hi I just figure it out after trying stuff (Wasn't able to understand reduce properly) this is another approach without using reduce and just using useEffect

const [libros, setLibros] = useState([]);
const [cantidad, setCantidad] = useState( new Array(libros.length).fill(1));
const [totalPrice, setTotalPrice] = useState([]);

useEffect((i) => {
        let x = 0
        libros.map((i, index) => {
          x += i.precio * cantidad[index]
          setTotalPrice(parseFloat(x).toFixed(2))
        })
      })

... some code

//And then just call the variable
<h1>Precio total: {totalPrice}</h1>

This is how it looks!

enter image description here

It also updates every time the "cantidad" goes up or down!

Upvotes: 1

Danial
Danial

Reputation: 1633

It's about two theoretical points.

  1. You must notice that map returns an array of values. So if you call a function, it will be call multiple times.

  2. In React, when state changes, component will re-render on every change. In map you call a function that changes state. Then state changing re-render all component. Again the map call that function , again and again. And this is an infinity loop.

If you want count total price, must do it outside of map return.

Separate logic. Do all countings outside of component return. Then in component return just show jsx elements

Upvotes: 1

Renan Felipe Ferreira
Renan Felipe Ferreira

Reputation: 318

As @Danial said, you're updating state so many times, very fastly, which triggers page renderization, and throw the error you got.

To do this summation of all items into the array, use reduce array function instead of map.

Reference: How to find the sum of an array of numbers

Upvotes: 1

Related Questions