DEV Community

Erick Eduardo Ramos
Erick Eduardo Ramos

Posted on

Cómo solucionar el bucle infinito en `useEffect` con objetos y arrays

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
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

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 {};
  });
}, []);
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

Bloque de código corregido (caso práctico)

Problema original:

const [ingredients, setIngredients] = useState({});

useEffect(() => {
  setIngredients({}); // ¡Bucle infinito!
}, [ingredients]);
Enter fullscreen mode Exit fullscreen mode

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]);
Enter fullscreen mode Exit fullscreen mode

Pro-tip

  • Nunca uses useEffect para "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({});
    };
  }, []);
Enter fullscreen mode Exit fullscreen mode

Top comments (0)