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 por referencia (===), no por contenido. Cuando usas useState({}), cada llamada a setObj({}) crea un nuevo objeto en memoria, aunque tenga el mismo contenido. Por lo tanto:
-
[obj]siempre cambia de referencia, incluso si el contenido es idéntico - React detecta un "cambio" y vuelve a ejecutar el
useEffect - Esto dispara
setObj({})de nuevo → bucle infinito
Este comportamiento es intencional en React para evitar costosas comparaciones profundas por defecto.
Solución definitiva (3 enfoques prácticos)
✅ Opción 1: Usar useReducer (recomendado para objetos complejos)
import { useReducer } from 'react';
const initialState = {};
const reducer = (state, action) => {
switch (action.type) {
case 'reset': return {};
default: return state;
}
};
function Component() {
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
dispatch({ type: 'reset' });
// No necesitas dependencias → se ejecuta solo en montaje
}, []); // ✅ Sin bucle infinito
}
✅ Opción 2: Comparación manual con useRef (para casos simples)
import { useState, useEffect, useRef } from 'react';
function Component() {
const [ingredients, setIngredients] = useState({});
const prevIngredientsRef = useRef();
useEffect(() => {
// Guardar valor actual antes de comparar
prevIngredientsRef.current = ingredients;
}, [ingredients]);
useEffect(() => {
// Solo ejecutar si el contenido es diferente
if (JSON.stringify(ingredients) !== JSON.stringify(prevIngredientsRef.current)) {
setIngredients({});
}
}, [ingredients]);
}
✅ Opción 3: Evitar mutaciones innecesarias (patrón más limpio)
useEffect(() => {
// Solo actualizar si no está vacío
if (Object.keys(ingredients).length > 0) {
setIngredients({});
}
}, [ingredients]);
Pro-tip: Regla de oro para dependencias con objetos/arrays
Nunca incluyas objetos/arrays directamente en las dependencias si vas a reasignarlos con el mismo valor.
En su lugar:
- Usa
useReducerpara lógica compleja- Compara propiedades específicas (
length,id,timestamp)- Usa
useMemopara crear referencias estables- Considera si realmente necesitas ese
useEffect(¿puede moverse al evento que lo dispara?)
Ejemplo de solución óptima para tu caso:
// Si solo necesitas resetear al montar el componente:
useEffect(() => {
setIngredients({});
}, []); // ✅ Dependencia vacía → ejecuta una vez
// Si necesitas resetear bajo condiciones específicas:
const resetIngredients = useCallback(() => {
setIngredients({});
}, []);
useEffect(() => {
if (someCondition) {
resetIngredients();
}
}, [someCondition, resetIngredients]);
⚠️ Evita
JSON.stringifyen dependencias (causa re-renders innecesarios y es ineficiente en objetos grandes).
Top comments (0)