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 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) y vuelve a ejecutar el efecto, causando el bucle infinito.

En tu caso:

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

useEffect(() => {
  setIngredients({}); // ← ¡Crea un nuevo objeto!
}, [ingredients]); // ← Siempre es una referencia diferente → bucle infinito
Enter fullscreen mode Exit fullscreen mode

Solución definitiva (3 enfoques)

Opción 1: Usar estado funcional + comparación profunda (recomendada)

useEffect(() => {
  setIngredients(prev => {
    // Solo actualiza si el contenido realmente cambió
    if (Object.keys(prev).length > 0) {
      return {};
    }
    return prev;
  });
}, []); // ← Sin dependencias: solo ejecuta al montar
Enter fullscreen mode Exit fullscreen mode

Opción 2: Comparar contenido manualmente con useDeepCompareEffect

// Implementa una comparación profunda personalizada
function useDeepCompareEffect(callback, dependencies) {
  useEffect(() => {
    callback();
  }, [dependencies.map(dep => JSON.stringify(dep))]);
}

// Uso:
useDeepCompareEffect(() => {
  setIngredients({});
}, [ingredients]);
Enter fullscreen mode Exit fullscreen mode

Opción 3: Usar useReducer para lógica compleja (mejor práctica)

const initialState = {};
function reducer(state, action) {
  switch (action.type) {
    case 'reset':
      return {};
    default:
      return state;
  }
}

const [ingredients, dispatch] = useReducer(reducer, initialState);

useEffect(() => {
  dispatch({ type: 'reset' });
}, []); // ← Sin dependencias
Enter fullscreen mode Exit fullscreen mode

Bloque de código corregido (recomendado)

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    // Solo resetea si el objeto no está vacío
    setIngredients(prev => 
      Object.keys(prev).length > 0 ? {} : prev
    );
  }, []); // ← Sin dependencias para evitar bucles

  return (
    <div>
      <p>Ingredients: {JSON.stringify(ingredients)}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Pro-tip: Regla de oro para objetos y arrays en useEffect

Nunca incluyas objetos/arrays directamente en las dependencias si vas a crear nuevos objetos/arrays dentro del efecto.

En su lugar:

  • Usa [] si la lógica solo necesita ejecutarse al montar/desmontar
  • Usa comparación profunda para dependencias dinámicas
  • Considera useReducer para estados complejos
  • Para validaciones simples, compara propiedades específicas (ej: ingredients.length en lugar del array completo)

Top comments (0)