I am getting this error:
caught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
import { BsFillCartFill } from "react-icons/bs";
import { useState} from "react";
import { addOrder } from "../api/orders";
import { updateManyProducts } from "../api/products";
import { useCartContext } from "../context/cartContext";
import { useAuthContext } from '../context/authContext';
import Swal from 'sweetalert2'
export const Cart = () => {
const { getTotal, cart, discount, emptyCart, removeProduct} = useCartContext();
const { user } = useAuthContext();
// Los RegEx (o expresiones regulares) permiten en este caso nos permiten validar que un telefono solo permita numeros, mas de 7 y menos de 15)
const phoneRegEx = /^+?[1-9][0-9]{7,14}$/;
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
const [orderState] = useState("generated");
const [phoneError, setPhoneError] = useState("")
const [stockError, setStockError] = useState("")
let dateObj = new Date();
let month = dateObj.getUTCMonth() + 1; //months from 1-12
let day = dateObj.getUTCDate();
let year = dateObj.getUTCFullYear();
let newdate = day + "/" + month + "/" + year;
// establezco en la variable email el correo de la cuenta que inicio sesión
setEmail(user.email)
// Función que será llamada en la siguiente funcion "validateInput" para validar datos con los RegEX declarados previamente
const validateRegEx = (value, regEx) => {
return String(value)
.toLowerCase()
.match(regEx);
};
const validatePhone = (value) => {
if (validateRegEx(value, phoneRegEx)) {
setPhone(value)
setPhoneError("")
} else {
setPhoneError("Telefono invalido")
}
}
if (cart.length <= 0)
return (
<div className="d-flex justify-content-evenly">
<div className="text-center my-5" style={{ fontWeight: 600 }}><BsFillCartFill /><br /><br /> Su carrito esta vacío</div>
</div>
);
// verifico la variable discount importada del cartContext y muestro el aviso de si se aplica o no
let discountSpan = ""
if (discount){
discountSpan =<span className="text-success">Descuento del 25% en la 3era misma unidad aplicado</span>}
else{
discountSpan =<span className="text-danger">No hay descuentos aplicados</span>}
// Filtra el cart a ver si algun item tiene mas cantidad del sotck disponible.
const noStockItems = cart.filter(item => item.qty>item.stock);
console.log(noStockItems[0])
// Mensaje de error si no hay stock disponible
const errorStock = (e) => {
e.preventDefault();
let itemsWithoutStock = ""
for (let item of noStockItems) {
itemsWithoutStock += " - " + item.nombre
}
setStockError("Ya no hay suficientes productos en stock para la cantidad elegida de " + itemsWithoutStock )
}
// Creamos la orden en firebase
const createOrder = async (e) => {
// Como esta función esta siendo llamada desde el boton tipo "submit" del form, el "e.preventDefault" esta previniendo el comportamiento por default que tiene el submit, el cual es un "get" request a la URL por defecto. Pasando en limpio, estamos previniendo ese comportamiento indeseado y definiendo, con las funciones a continuación, que sucede cuando se clickea en submit .
e.preventDefault();
// Armamos un nuevo array con 4 propiedades de cada objeto del array "cart" para facilitar el llamado
const items = cart.map(({ id, nombre, qty, valor, color }) => ({
id,
nombre,
qty,
valor,
color
}));
let itemsAlert = ""
// Recorremos el array items e incluimos en un string el mensaje que nos aparecerá en nuestra alerta cuando realicemos la orden
for (let i = 0; i < items.length; i++) {
itemsAlert += "<b>Item:</b> " + items[i].nombre + "<br><b>Cantidad:</b> " + items[i].qty +
"<br><b>Color:</b> " + items[i].color +" <b>Valor Unidad:</b> $" + items[i].valor + "<br><br>" ;
}
// creamos la orden como un objeto
const order = {
buyer: { name, phone, email },
items,
fecha: newdate,
estado: { orderState },
total: getTotal(),
};
// desestructuramos ciertas propiedades de la orden para llamarlas con mas facilidad
const { buyer, fecha, total } = order
// Agregamos la orden a la colección "orders" de firebase, y luego se nos retorna el "id" de la misma
const id = await addOrder(order);
// Actualizamos el stock de los productos en firebase
await updateManyProducts(items);
emptyCart();
// Alerta de orden realizada
Swal.fire(
{
title: "Pedido Realizado",
html: `El id de su compra es <b>"${id}"</b> <br> Fecha: ${fecha}<br><br> <b>Detalle de compra:</b><br> <br> <b>Comprador:</b> ${buyer.name} <br><br> ${itemsAlert}<br> <b>Total: $${total}</b>`,
icon: 'success',
confirmButtonText: 'OK'
})
};
return (
<div className=" m-5">
<h2 className="text-center my-5"><BsFillCartFill /> Carrito <BsFillCartFill /> </h2>
{cart.map((product) => (
<div key={product.id}
style={{
display: "flex",
gap: 50,
height: 100,
alignItems: "center",
justifyContent: "space-evenly",
}} className="m-auto">
<div className="h-50"><img className="img-fluid h-100" src={product.img} alt="" /></div>
<div>Producto : <b><b>{product.nombre}</b></b></div>
<div>Valor unitario : <b><b>${product.valor}</b></b></div>
<div>Cantidad : <b><b>{product.qty}</b></b></div>
<div>Color : <b><b>{product.color}</b></b></div>
<button className="border-5 rounded-5 bg-dark text-white"
onClick={(e) => {
e.stopPropagation();
removeProduct(product.id, product.color)
}}
>Eliminar</button>
</div>
))}
<div className="mx-auto my-4">
<span style={{
marginBottom: 50,
textAlign: "center",
width: "70%",
fontSize: 20,
}}>
{discountSpan} <br />
Total : <b><b>${getTotal()}</b></b>
</span>
</div>
<div className=" m-auto my-5 text-center">
<button className="border-5 rounded-5 bg-dark text-white"
onClick={() => emptyCart()}
>Vaciar carrito</button>
</div>
<form style={{ display: "grid", gap: 10 }} className="my-5" >
<span className="text-center col-12" >Estas realizando el pedido con el email de tu cuenta <br /><br /><b> {email} </b></span>
<span>Nombre y apellido</span>
<input
style={{ border: "1px solid black", height: 40 }}
onChange={(e) => setName(e.target.value)}
/>
<span>Telefono</span>
<input style={{ border: "1px solid black", height: 40 }} onBlur={(e) => validatePhone(e.target.value)}/>
<input type="submit" value="Enviar" onClick={noStockItems.length > 0 ? errorStock : createOrder} />
</form>
<div>
{stockError}
<br />
{phoneError}
</div>
</div>
);
};
The code was working fine and I made a few changes in the form (previously you needed to set an email but now I wanted to use the one I get with the account from firebase). I definitely touched something and now is broken. I can´t find what´s worng even with chat gpt. Please help, thanks everyone!
2
Answers
Likely due to
setEmail(user.email)
as you are setting the email on every render, causing a new render.Move your
setEmail(user.email)
inside theuseEffect
hook