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 de referencia (===), no por contenido (deep equality). 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), lo que dispara la reejecución del useEffect, causando el bucle infinito.

En tu caso:

const [ingredients, setIngredients] = useState({});
useEffect(() => {
  setIngredients({}); // ¡Crea un nuevo objeto!
}, [ingredients]); // ingredients cambia de referencia → reejecuta → loop infinito
Enter fullscreen mode Exit fullscreen mode

Pasos para solucionarlo

Opción 1: Evitar la actualización innecesaria (recomendada)

Si no necesitas actualizar el estado dentro del useEffect, elimina la dependencia del objeto/array y usa un array vacío:

useEffect(() => {
  // Solo ejecutar al montar el componente
  setIngredients({});
}, []); // ✅ Sin dependencias → solo se ejecuta una vez
Enter fullscreen mode Exit fullscreen mode

Opción 2: Comparación profunda manual

Si necesitas ejecutar el useEffect solo cuando el contenido cambia, usa JSON.stringify (para objetos simples sin funciones/circularidades):

useEffect(() => {
  setIngredients({});
}, [JSON.stringify(ingredients)]); // ✅ Compara contenido, no referencia
Enter fullscreen mode Exit fullscreen mode

⚠️ Advertencia: JSON.stringify es costoso en objetos grandes y falla con funciones, undefined, o referencias circulares.

Opción 3: Usar useMemo para una representación estable

Crea una representación estable del objeto (ej. número de elementos, hash):

const ingredientsHash = useMemo(() => 
  Object.keys(ingredients).length, // o tu lógica personalizada
  [ingredients]
);

useEffect(() => {
  setIngredients({});
}, [ingredientsHash]); // ✅ Compara valor primitivo estable
Enter fullscreen mode Exit fullscreen mode

Opción 4: Validar si el valor es distinto antes de actualizar

Evita la actualización si el contenido es idéntico:

useEffect(() => {
  if (Object.keys(ingredients).length > 0) {
    setIngredients({});
  }
}, [ingredients]); // ✅ Solo actualiza si no está vacío
Enter fullscreen mode Exit fullscreen mode

Bloque de código corregido (recomendado)

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

useEffect(() => {
  // Solo ejecutar al montar el componente (ej. limpiar estado inicial)
  setIngredients({});
}, []); // ✅ Solución definitiva para este caso
Enter fullscreen mode Exit fullscreen mode

Pro-tip

  • Nunca actualices el mismo estado que usas como dependencia sin una lógica de comparación profunda.
  • Para objetos complejos, usa librerías como immer o useDeepCompareEffect (de use-deep-compare-effect) si necesitas comparación profunda segura.
  • Si el objetivo es limpiar el estado al montar, usa useEffect con [] y no actualices el estado dentro. En su lugar, inicializa el estado con el valor deseado desde el inicio:
const [ingredients, setIngredients] = useState({}); // ✅ Inicialización directa
Enter fullscreen mode Exit fullscreen mode

Top comments (0)