Explicación técnica #
El problema ocurre porque useEffect
compara los valores de las dependencias usando comparación de referencia ( ===), no por contenido (deep equality). Cuando usas
useState({})
, cada llamada a setObj({})
crea un nuevo objeto en memoria, aunque tenga el mismo contenido. React detecta que la referencia cambia (
obj !== obj
), lo que dispara la reejecución del useEffect
, causando el bucle infinito.En tu caso:
const [ingredients, setIngredients] = useState({});
useEffect(() => {
setIngredients({}); // ¡Crea un nuevo objeto!
}, [ingredients]); // ingredients cambia de referencia → reejecuta → loop infinito
Pasos para solucionarlo #
Opción 1: Evitar la actualización innecesaria (recomendada)
Si no necesitas actualizar el estado dentro del useEffect
, elimina la dependencia del objeto/array y usa un array vacío:
useEffect(() => {
// Solo ejecutar al montar el componente
setIngredients({});
}, []); // ✅ Sin dependencias → solo se ejecuta una vez
Opción 2: Comparación profunda manual
Si necesitas ejecutar el useEffect
solo cuando el contenido cambia, usa JSON.stringify
(para objetos simples sin funciones/circularidades):
useEffect(() => {
setIngredients({});
}, [JSON.stringify(ingredients)]); // ✅ Compara contenido, no referencia
⚠️ Advertencia: JSON.stringify
es costoso en objetos grandes y falla con funciones, undefined
, o referencias circulares.
Opción 3: Usar useMemo
para una representación estable
Crea una representación estable del objeto (ej. número de elementos, hash):
const ingredientsHash = useMemo(() =>
Object.keys(ingredients).length, // o tu lógica personalizada
[ingredients]
);
useEffect(() => {
setIngredients({});
}, [ingredientsHash]); // ✅ Compara valor primitivo estable
Opción 4: Validar si el valor es distinto antes de actualizar
Evita la actualización si el contenido es idéntico:
useEffect(() => {
if (Object.keys(ingredients).length > 0) {
setIngredients({});
}
}, [ingredients]); // ✅ Solo actualiza si no está vacío
Bloque de código corregido (recomendado) #
const [ingredients, setIngredients] = useState({});
useEffect(() => {
// Solo ejecutar al montar el componente (ej. limpiar estado inicial)
setIngredients({});
}, []); // ✅ Solución definitiva para este caso
Pro-tip #
Nunca actualices el mismo estado que usas como dependencia sin una lógica de comparación profunda. - Para objetos complejos, usa librerías como
immer
ouseDeepCompareEffect
(deuse-deep-compare-effect
) si necesitas comparación profunda segura. - Si el objetivo es limpiar el estado al montar, usa
useEffect
con[]
y no actualices el estado dentro. En su lugar, inicializa el estado con el valor deseado desde el inicio:
const [ingredients, setIngredients] = useState({}); // ✅ Inicialización directa