Cómo solucionar el bucle infinito en useEffect con objetos y arrays
Explicación técnica (por qué ocurre)
El bucle infinito ocurre porque React compara las dependencias del useEffect usando comparación === (igualdad estricta), no por contenido. Cuando usas useState({}), cada llamada a setObj({}) crea un nuevo objeto en memoria, aunque tenga el mismo contenido. Como {} !== {}, React detecta un cambio y vuelve a ejecutar el useEffect, causando el ciclo infinito.
Este comportamiento es intencional: React asume que si el objeto es distinto en referencia, su contenido podría haber cambiado, y no hace una comparación profunda por rendimiento.
Pasos para solucionarlo
Opción 1: Evitar reasignar el mismo valor (recomendada)
Si el objetivo es inicializar el estado, hazlo solo una vez (en el montaje), no en cada render.
// ✅ CORRECTO: Solo se ejecuta una vez al montar el componente
useEffect(() => {
setIngredients({});
}, []); // Dependencia vacía: solo en mount/unmount
Opción 2: Comparar contenido manualmente con useDeepCompareEffect
Si necesitas reactividad basada en el contenido del objeto/array:
- Instala una librería como
use-deep-compare-effect:
npm install use-deep-compare-effect
- Úsala en lugar de
useEffect:
import useDeepCompareEffect from 'use-deep-compare-effect';
useDeepCompareEffect(() => {
setIngredients({});
}, [ingredients]); // Ahora compara contenido, no referencia
Opción 3: Normalizar el estado (patrón recomendado)
Evita objetos/array como dependencias directas. Usa valores primitivos o claves específicas:
// En lugar de depender de todo el objeto
const [ingredients, setIngredients] = useState({});
const [ingredientsCount, setIngredientsCount] = useState(0);
useEffect(() => {
setIngredients({});
setIngredientsCount(0); // O actualiza según lógica real
}, [ingredientsCount]); // Dependencia primitiva → no hay bucle infinito
Bloque de código corregido (ejemplo práctico)
Antes (bucle infinito):
const [ingredients, setIngredients] = useState({});
useEffect(() => {
setIngredients({}); // ❌ Crea nuevo objeto → nueva referencia → bucle
}, [ingredients]); // ❌ Siempre cambia la referencia
Después (solución definitiva):
const [ingredients, setIngredients] = useState({});
// ✅ Opción 1: Si solo quieres limpiar al montar
useEffect(() => {
setIngredients({});
}, []); // Dependencia vacía
// ✅ Opción 2: Si necesitas limpiar bajo condiciones específicas
const resetIngredients = useCallback(() => {
setIngredients({});
}, []);
useEffect(() => {
// Lógica para decidir cuándo resetear (ej: cuando se cumple una condición)
if (someCondition) {
resetIngredients();
}
}, [someCondition, resetIngredients]);
Pro-tip: Evita este error desde el diseño
-
Usa
useReducerpara estados complejos: Manejar objetos/array conuseReducerevita este problema y mejora la trazabilidad:
const initialState = {};
function reducer(state, action) {
switch (action.type) {
case 'reset': return {};
default: return state;
}
}
const [state, dispatch] = useReducer(reducer, initialState);
Sigue el principio de "normalización":
En lugar de depender de objetos completos, extrae valores clave (IDs, longitudes, hashes) para las dependencias.Nunca pongas objetos/array literales en las dependencias:
useEffect(() => {}, [{a: 1}])siempre causará bucles infinitos.
Regla de oro: Si usas
useState({}), el único lugar donde debes llamarsetObj({})es en eluseEffectcon dependencia vacía[], o dentro de unuseCallback/useMemocon dependencias estables.
Top comments (0)