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

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 (por qué ocurre) El problema ocurre porque React compara los dependencies del useEffect usando comparación === (igualdad estricta), no una comparación profunda del contenido. Cuando haces: const [obj, setObj] = useState({}); useEffect(() => { setObj({}); }, [obj]); Enter fullscreen mode Exit fullscreen mode Cada vez que setObj({}) se ejecuta, crea un nuevo objeto en memoria. Aunque el contenido parece idéntico, {} !== {} siempre es true. Por lo tanto: useEffect detecta que obj ha cambiado (nueva referencia) Ejecuta la callback → setObj({}) crea otro nuevo objeto React vuelve a detectar cambio de referencia → bucle infinito Solución definitiva (3 enfoques prácticos) ✅ Opción 1: Evita el ciclo usando dependencias vacías (si solo necesitas ejecutar al montar) useEffect(() => { setIngredients({}); }, []); // Solo se ejecuta una vez al montar el componente Enter fullscreen mode Exit fullscreen mode ✅ Opción 2: Usa comparación profunda con JSON.stringify (para efectos que dependen del contenido) useEffect(() => { // Solo ejecutar si el contenido de ingredients ha cambiado if (JSON.stringify(ingredients) !== JSON.stringify(prevIngredientsRef.current)) { // Tu lógica aquí } }, [ingredients]); Enter fullscreen mode Exit fullscreen mode ⚠️ Nota: JSON.stringify tiene limitaciones (no preserva funciones, undefined, Symbol, orden de claves no garantizado). Úsalo solo para objetos simples. ✅ Opción 3: Usa useReducer + estado inmutable (recomendado para lógica compleja) const initialState = {}; const reducer = (state, action) => { switch (action.type) { case 'reset': return {}; // Devuelve nuevo objeto, pero controlado default: return state; } }; const [ingredients, dispatch] = useReducer(reducer, initialState); useEffect(() => { dispatch({ type: 'reset' }); }, []); // O con lógica específica según tu caso Enter fullscreen mode Exit fullscreen mode Bloque de código corregido (ejemplo práctico) Antes (bucle infinito): const [ingredients, setIngredients] = useState({}); useEffect(() => { setIngredients({}); // ❌ Crea nuevo objeto → bucle infinito }, [ingredients]); // ❌ Referencia cambia siempre Enter fullscreen mode Exit fullscreen mode Después (solución limpia): const [ingredients, setIngredients] = useState({}); useEffect(() => { // ✅ Solo resetear si no está vacío (evita ciclos innecesarios) if (Object.keys(ingredients).length > 0) { setIngredients({}); } }, [ingredients]); Enter fullscreen mode Exit fullscreen mode Pro-tip: Patrón para evitar este error en producción Nunca uses objetos/arrays literales como dependencias directas ([{}], [[]]) Usa useRef para valores que no deben desencadenar re-renders: const ingredientsRef = useRef({}); useEffect(() => { ingredientsRef.current = {}; // Modifica sin causar re-renders }, []); Enter fullscreen mode Exit fullscreen mode Para objetos complejos, usa librerías como immer o useDeepCompareEffect (de use-deep-compare-effect): import useDeepCompareEffect from 'use-deep-compare-effect'; useDeepCompareEffect(() => { setIngredients({}); }, [ingredients]); // ✅ Compara contenido, no referencias Enter fullscreen mode Exit fullscreen mode 🔥 Regla de oro: Si tu useEffect modifica el mismo estado que usa como dependencia, siempre debes verificar si el cambio es necesario antes de llamar al setter.

📰 Original Source

Read full article at Dev →

KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.