Cómo solucionar el bucle infinito en useEffect con objetos y arrays
Explicación técnica
El problema ocurre porque useEffect compara los valores de las dependencias usando comparación por referencia (===), no por contenido. 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), por lo que vuelve a ejecutar el efecto, causando el bucle infinito.
En tu caso:
const [ingredients, setIngredients] = useState({});
useEffect(() => {
setIngredients({}); // ¡Crea un nuevo objeto!
}, [ingredients]); // Siempre cambia la referencia → bucle infinito
Solución definitiva
Opción 1: Evitar la actualización innecesaria (recomendada)
Verifica si el objeto/array ya tiene el estado deseado antes de actualizarlo:
useEffect(() => {
if (Object.keys(ingredients).length > 0) {
setIngredients({});
}
}, [ingredients]);
Opción 2: Usar useMemo para mantener referencias estables
Si necesitas resetear el objeto, usa una función de actualización y compara el contenido:
useEffect(() => {
setIngredients(prev => {
// Solo actualiza si realmente hay cambios
if (Object.keys(prev).length === 0) return prev;
return {};
});
}, []);
Opción 3: Comparar contenido profundo (cuando sea necesario)
Si necesitas detectar cambios reales en el contenido:
import { useEffect, useState, useRef } from 'react';
import _ from 'lodash'; // o usa tu propia función de comparación
const [ingredients, setIngredients] = useState({});
// Guarda el valor anterior para comparación
const prevIngredientsRef = useRef(ingredients);
useEffect(() => {
// Comparación profunda (ajustar según tu caso)
if (!_.isEqual(ingredients, prevIngredientsRef.current)) {
// Haz algo solo si hay cambios reales
prevIngredientsRef.current = ingredients;
}
}, [ingredients]);
Bloque de código corregido (caso práctico)
Problema original:
const [ingredients, setIngredients] = useState({});
useEffect(() => {
setIngredients({}); // ¡Bucle infinito!
}, [ingredients]);
Solución definitiva (resetear solo si no está vacío):
const [ingredients, setIngredients] = useState({});
useEffect(() => {
// Solo resetea si el objeto no está vacío
if (Object.keys(ingredients).length > 0) {
setIngredients({});
}
}, [ingredients]);
Pro-tip
-
Nunca uses
useEffectpara "mantener sincronizado" un estado con su valor inicial. Siempre pregunta: ¿por qué necesito actualizar esto? ¿Qué evento real lo desencadena? - Para objetos/arrays, usa dependencias específicas (ej:
ingredients.length,ingredients.id) en lugar de la referencia completa. - Si el objetivo es limpiar el estado al desmontar, usa el cleanup function de
useEffect:
useEffect(() => {
return () => {
setIngredients({});
};
}, []);
Top comments (0)