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
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]);
2. Comparación profunda manual
Usa JSON.stringify para comparar contenido (solo para objetos simples):
useEffect(() => {
setIngredients({});
}, [JSON.stringify(ingredients)]);
⚠️ 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]);
Bloque de código corregido
Antes (bucle infinito):
const [ingredients, setIngredients] = useState({});
useEffect(() => {
setIngredients({}); // ← ¡Causa bucle infinito!
}, [ingredients]);
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
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:
- Usa
useRefpara rastrear si es la primera ejecución - Implementa lógica condional con
useMemopara cálculos derivados - Para listas/arrays, usa
useCallbackconuseMemopara 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]);












