Cómo solucionar el bucle infinito en useEffect con objetos y arrays
Explicación técnica
El problema ocurre porque useEffect compara 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, por lo que [obj] siempre cambia de referencia aunque su contenido sea idéntico.
En tu caso:
const [obj, setObj] = useState({});
useEffect(() => {
setObj({}); // ¡Nuevo objeto en memoria!
}, [obj]); // obj referencia al objeto anterior → desencadena re-renders infinitos
React detecta que obj (referencia) ha cambiado → ejecuta el efecto → llama a setObj({}) → crea nuevo objeto → obj cambia de referencia → se repite el ciclo.
Pasos para solucionarlo
Opción 1: Evitar el ciclo con dependencia vacía (si no necesitas reactividad)
useEffect(() => {
setIngredients({});
}, []); // Solo se ejecuta una vez al montar el componente
Opción 2: Comparación profunda manual (si necesitas reactividad)
useEffect(() => {
// Solo ejecutar si el contenido cambió
if (JSON.stringify(ingredients) !== '{}') {
setIngredients({});
}
}, [ingredients]);
Opción 3: Usar useReducer para estado complejo (recomendado para objetos/arrays)
const initialState = { /* tu estado inicial */ };
function reducer(state, action) {
switch (action.type) {
case 'reset':
return {};
default:
return state;
}
}
const [ingredients, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
dispatch({ type: 'reset' });
}, [ingredients]);
Opción 4: Normalizar el estado con valores primitivos
const [ingredientsCount, setIngredientsCount] = useState(0);
useEffect(() => {
if (ingredientsCount > 0) {
setIngredientsCount(0); // Primitivo → comparación por valor
}
}, [ingredientsCount]);
Bloque de código corregido (recomendado)
import { useState, useEffect, useCallback } from 'react';
function Component() {
const [ingredients, setIngredients] = useState({});
// Solución definitiva: memoizar la función para evitar recreaciones
const resetIngredients = useCallback(() => {
setIngredients({});
}, []);
useEffect(() => {
// Solo resetear si el objeto no está vacío
if (Object.keys(ingredients).length > 0) {
resetIngredients();
}
}, [ingredients, resetIngredients]);
return (
/* tu JSX */
);
}
Pro-tip: Reglas de oro para evitar este error
Nunca uses objetos/arrays literales como dependencias directas
useEffect(..., [{ a: 1 }])siempre fallará.-
Para objetos/arrays, usa comparación profunda o normaliza el estado
-
JSON.stringify()para objetos simples (evita en paths críticos por rendimiento) - Librerías como
lodash.isEqualpara casos complejos -
useMemopara calcular hashes o valores primitivos derivados
-
Preferir
useReducerpara lógica de estado compleja
Reduce la dependencia de efectos reactivos y mejora el control del flujo.Siempre pregunta: "¿Necesito este efecto reaccionar a todos los cambios del objeto o solo a cambios específicos?"
Filtra las dependencias a los valores primitivos que realmente importan (ej:ingredients.lengthen lugar deingredients).
Top comments (0)