close

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 estricta (===), no por contenido. Cuando usas useState({}), cada llamada a setObj({}) crea un nuevo objeto en memoria, aunque tenga el mismo contenido. React detecta que obj !== obj (referencias diferentes), por lo que ejecuta el efecto infinitamente.

const [obj, setObj] = useState({});
useEffect(() => {
  setObj({}); // ← ¡Nuevo objeto en memoria!
}, [obj]); // ← Referencia diferente → bucle infinito
Enter fullscreen mode Exit fullscreen mode

Solución definitiva (3 enfoques)

1. Evitar la actualización innecesaria (Recomendado)

Si no necesitas actualizar el estado, no llames al setter dentro del useEffect:

useEffect(() => {
  // Solo ejecutar lógica si es necesario
  if (Object.keys(ingredients).length === 0) {
    // Hacer algo solo si está vacío, pero NO setear de nuevo
  }
}, [ingredients]);
Enter fullscreen mode Exit fullscreen mode

2. Comparación profunda manual

Usa JSON.stringify para comparar contenido (solo para objetos simples):

useEffect(() => {
  setIngredients({});
}, [JSON.stringify(ingredients)]);
Enter fullscreen mode Exit fullscreen mode

⚠️ Advertencia: No usar en objetos grandes o con funciones/métodos.

3. Estado inmutable con clonación controlada

Si necesitas resetear el estado, usa una bandera o lógica condicional:

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

useEffect(() => {
  if (shouldReset) {
    setIngredients({});
    setShouldReset(false); // Resetear bandera
  }
}, [shouldReset]);
Enter fullscreen mode Exit fullscreen mode

Bloque de código corregido

Antes (bucle infinito):

const [ingredients, setIngredients] = useState({});
useEffect(() => {
  setIngredients({}); // ← ¡Causa bucle infinito!
}, [ingredients]);
Enter fullscreen mode Exit fullscreen mode

Después (solución limpia):

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

useEffect(() => {
  // Solo ejecutar si hay lógica real que necesite ejecutarse
  // Si solo quieres resetear, hazlo desde un evento o efecto controlado
}, []); // ← Dependencia vacía si solo necesitas ejecutar al montar
Enter fullscreen mode Exit fullscreen mode

Pro-tip: Patrón de diseño recomendado

Nunca actualices el mismo estado que estás observando en el useEffect sin una condición clara de salida. En su lugar:

  1. Usa useRef para rastrear si es la primera ejecución
  2. Implementa lógica condional con useMemo para cálculos derivados
  3. Para listas/arrays, usa useCallback con useMemo para evitar re-renders innecesarios
const [data, setData] = useState([]);

// Ejemplo: Solo actualizar si hay cambios reales
const prevDataRef = useRef(data);
useEffect(() => {
  if (prevDataRef.current !== data) {
    // Lógica solo si cambió
    prevDataRef.current = data;
  }
}, [data]);
Enter fullscreen mode Exit fullscreen mode

Top comments (0)