Contenido del curso
Maquetación con React.js
Interacción con React.js
Librería de Iconos Personalizados
Herramientas avanzadas: escalabilidad, organización y persistencia
- 13

Persistencia de datos con localStorage en React
25:48 min - 14

Custom Hook para Local Storage en React
17:53 min - 15

Cómo organizar carpetas de componentes React
07:40 min - 16

Feature-First Directories en React
09:12 min - 17

Tips para naming y abstracción de componentes React
12:24 min - 18

useEffect para controlar renders costosos
14:04 min - 19

Estados de carga y error
15:04 min - 20

Cómo evitar el bucle infinito en useEffect
Viendo ahora - 21

Loading Skeletons animados en React con CSS
11:59 min - 22

¿Qué es React Context?
20:57 min - 23

useContext
10:47 min - 24

¿Qué son los React Portals?
14:03 min - 25

Toggle de modales con estado previo en React
03:33 min - 26

Formularios en React sin recargar página
15:08 min - 27

Cómo crear addTodo con Context API
15:15 min
Deploy
Próximos pasos: React #UnderTheHood
Bonus: creando proyectos en React desde cero
Cómo evitar el bucle infinito en useEffect
Resumen
Manejar los estados de carga y error en React puede convertirse en una pesadilla si no proteges tu useEffect correctamente. Aquí verás cómo actualizar los estados de loading y error dentro de un custom hook con localStorage, envolver la lógica en un bloque try/catch, y evitar el temido bucle infinito que rompe tu aplicación.
Por qué mi estado nunca se actualiza desde localStorage
Al construir un custom hook para localStorage, es común crear los estados con React.useState y olvidar actualizarlos. Si defines item, loading y error con valores iniciales pero nunca llamas a setItem, setLoading o setError, tu aplicación se queda congelada en el estado inicial.
La solución dentro del efecto es directa: cuando termine la lectura de localStorage, llama a setLoading(false) para avisar que ya no estás cargando. Y si el valor parseado es distinto al initial value, llama a setItem(parsedItem) para reflejar los datos reales.
¿Por qué mi useState no refleja los datos de localStorage? Porque el estado se queda con el initial value si nunca invocas el setter. Debes llamar a
setItem(parsedItem)dentro del efecto una vez que hayas leído y parseado la información.
Cómo capturar errores con try catch
localStorage puede fallar en cualquier momento y no controlas cuándo. Por eso envuelves la lógica de lectura en un bloque try y, si algo sale mal, entras al catch para actualizar el estado de error.
Dentro del catch tienes dos opciones:
- Enviar el error tal cual con
setError(error)si quieres saber el detalle. - Enviar un booleano
setError(true)si solo necesitas indicar que algo falló.
Si vas a mostrar el mensaje a usuarios finales, quédate con un genérico tipo Hubo un error. Y no olvides que cuando cae en el catch también debes hacer setLoading(false), porque ya terminó el intento aunque haya fallado.
Cómo evitar el bucle infinito en useEffect
Una vez que agregas un setTimeout de 2000 milisegundos para simular una llamada asíncrona (como si consultaras una API REST), React deja de gritar con maximum update depth exceeded. Pero eso no significa que tu código esté sano.
El efecto sigue siendo un bucle infinito silencioso: el estado inicial dispara el efecto, el efecto actualiza el estado con setItem, setLoading o setError, ese cambio provoca un nuevo render, el hook se vuelve a llamar y el ciclo empieza otra vez. React no lo marca como error porque el setTimeout lo hace ver natural, pero está mal.
¿Cómo hago que useEffect se ejecute solo una vez? Debes pasarle un array de dependencias vacío como segundo argumento:
useEffect(() => {}, []). Son tres caracteres, un espacio incluido, que se olvidan facilísimo.
Qué son los ciclos de vida y por qué importa useEffect
Antes de los hooks, React trabajaba con clases que extendían de React.Component y usaban métodos de ciclo de vida como componentDidMount o componentDidUpdate. Con las versiones más recientes, useEffect se convirtió en la alternativa funcional a esos métodos.
Es cómodo, pero también engañoso: olvidar el array de dependencias es un error tan común como difícil de detectar cuando no salta ninguna alerta roja en la consola.
Cómo depurar el famoso undefined en filter
Otro error típico que aparece en esta implementación es intentar llamar a .filter sobre algo que no es un array. La consola dice claramente que no puede leer propiedades de undefined, y eso apunta a que el estado derivado (searchedTodos, por ejemplo) está recibiendo basura.
Cuando pasa esto, sigue este flujo de depuración:
- Añade un
console.logen el custom hook antes delreturnpara ver qué valor tiene realmenteitem. - Revisa la línea exacta que apunta el error en el archivo
useLocalStorage. - Verifica que los parámetros de la función estén bien declarados.
En este caso, el bug estaba en la firma del hook: por probar ejemplos, quedó escrito useLocalStorage(itemName, [initialValue]) con corchetes alrededor de initialValue. Eso convierte el parámetro en un patrón de array destructuring y rompe el valor recibido. La corrección es dejarlo como un parámetro normal: useLocalStorage(itemName, initialValue).
Cuándo borrar el localStorage en desarrollo
Si sospechas que el problema viene de datos corruptos guardados en el navegador, puedes borrar todo el localStorage desde la consola. En producción con usuarios reales esto sería catastrófico, pero en desarrollo es una herramienta válida para volver a un estado limpio y ejecutar los default todos que siempre funcionan.
Eso sí, si el error persiste después de limpiar, la falla no está en los datos sino en tu lógica, como pasó con el parámetro entre corchetes.
Cómo simular una carga asíncrona con setTimeout
Para imitar el comportamiento de una petición real y ver los estados de carga en acción, envuelve toda la lógica del try/catch dentro de un setTimeout con un delay de 2000 milisegundos. No uses setInterval, porque ese ejecutaría la función repetidamente.
La función que pasas como primer argumento contiene tu bloque try/catch completo, y el segundo argumento define cuántos milisegundos esperar antes de ejecutarla. Con esto, al recargar la aplicación verás por dos segundos el mensaje Estamos cargando antes de que aparezcan los TODOs, exactamente como pasaría con una API real.
¿Ya encontraste los tres caracteres que resuelven el bucle infinito en tu propio código? Cuéntame en los comentarios cómo llegaste a la solución o qué caminos exploraste durante la búsqueda.