Cómo solucionar el bucle infinito en useEffect con objetos y arrays
Explicación técnica
El problema ocurre porque React compara las dependencias del useEffect usando comparación de referencia (===), no por valor. Cuando usas useState({}), cada llamada a setObj({}) crea un nuevo objeto en memoria, aunque tenga el mismo contenido. Al pasar [obj] como dependencia, React detecta que la referencia del objeto cambia en cada render (porque es un objeto distinto), lo que dispara el useEffect de nuevo, creando un bucle infinito.
Este comportamiento es intencional: los objetos y arrays son tipos por referencia en JavaScript, y React no hace deep comparison por rendimiento.
Pasos para solucionarlo
Opción 1: Evitar la actualización innecesaria (recomendada)
No actualices el estado si no hay cambios reales. Usa una condición para verificar si realmente necesitas hacer el set:
useEffect(() => {
// Solo actualizar si el objeto no está vacío
if (Object.keys(ingredients).length > 0) {
setIngredients({});
}
}, [ingredients]);
Opción 2: Usar useMemo para mantener la misma referencia
Si necesitas mantener un objeto/array constante en memoria:
const emptyIngredients = useMemo(() => ({}), []);
useEffect(() => {
setIngredients(emptyIngredients);
}, [emptyIngredients]);
Opción 3: Comparar contenido manualmente con JSON.stringify (solo para estructuras simples)
⚠️ Solo para objetos planos sin funciones ni métodos
useEffect(() => {
const current = JSON.stringify(ingredients);
const target = JSON.stringify({});
if (current !== target) {
setIngredients({});
}
}, [ingredients]);
Opción 4: Usar useReducer para lógica compleja
Para estados complejos, useReducer es más adecuado y evita este tipo de problemas:
const initialState = { items: {} };
function reducer(state, action) {
switch (action.type) {
case 'reset':
return { ...state, items: {} };
default:
return state;
}
}
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
// Lógica que decide cuándo resetear
if (shouldReset(state.items)) {
dispatch({ type: 'reset' });
}
}, [state.items]);
Bloque de código corregido (solución definitiva)
La solución más limpia y performante para tu caso específico:
const [ingredients, setIngredients] = useState({});
useEffect(() => {
// Solo actualizar si hay algo que resetear
if (Object.keys(ingredients).length > 0) {
setIngredients({});
}
// No incluir ingredients en las dependencias para evitar el bucle
}, []);
Pro-tip
-
Nunca incluyas objetos/arrays directamente como dependencias si vas a crear nuevos objetos en el
useEffect - Usa
useMemopara mantener referencias estables cuando necesites comparar objetos - Para arrays, considera usar
useMemoconJSON.stringifyo bibliotecas comoimmerpara manejo de estado inmutable - Si el estado es complejo,
useReduceres casi siempre mejor que múltiplesuseStatecon efectos interdependientes
Top comments (0)