Contenido del curso

Herramientas avanzadas: escalabilidad, organización y persistencia

Persistencia de datos con localStorage en React

Resumen

Aprende a implementar localStorage en una aplicación JavaScript para que los datos sobrevivan al cierre del navegador. Esta guía explica cómo guardar, leer y eliminar información persistente, transformando arrays y objetos con JSON para que tu app recuerde el estado del usuario.

Qué es localStorage y para qué sirve

localStorage es una API del navegador que permite persistencia de datos incluso si el usuario cierra la pestaña, la ventana o apaga el computador [00:05]. Cuando vuelve a abrir tu aplicación, la información sigue ahí.

En una app de todos, esto evita que la lista vuelva al array por defecto cada vez que se recarga. Cada cambio se guarda tanto en el estado de React como en localStorage, y al cargar la app, el estado inicial se sincroniza con lo que ya estaba guardado.

¿Qué es localStorage? Es una API del navegador que guarda pares clave-valor como strings, manteniéndolos disponibles aunque el usuario cierre el navegador o reinicie su equipo.

Cómo leer, guardar y eliminar datos en localStorage

La API expone tres métodos principales que vas a usar todo el tiempo. No funciona como una asignación normal de JavaScript: necesitas métodos específicos.

  • localStorage.getItem('clave'): lee el valor guardado bajo esa clave [02:10].
  • localStorage.setItem('clave', 'valor'): guarda un string bajo esa clave [02:30].
  • localStorage.removeItem('clave'): elimina ese item del almacenamiento [03:20].

Si escribes localStorage en la consola, ves todo lo que hay guardado actualmente. Y aquí viene lo interesante: cualquier cosa que guardes tiene que ser un string. Si intentas guardar un array directamente, te va a aparecer el famoso "[object Object]" y nada va a funcionar.

Por qué necesitas JSON.stringify y JSON.parse

Como localStorage solo acepta strings, necesitas dos herramientas para convertir estructuras de JavaScript:

  • JSON.stringify(valor): convierte un array u objeto en un string que sí se puede guardar [07:15].
  • JSON.parse(string): convierte ese string de vuelta a un array u objeto utilizable [08:30].

Si guardas un array de cinco elementos sin stringify, al leerlo vas a obtener algo de 250 caracteres en lugar de cinco items. La regla es simple: stringify al guardar, parse al leer.

¿Por qué localStorage solo guarda strings? Porque su especificación lo limita a pares clave-valor de tipo string. Para guardar arrays u objetos necesitas serializarlos con JSON.stringify y deserializarlos con JSON.parse.

Cómo conectar localStorage con el estado inicial de React

El flujo correcto en una app React es leer localStorage primero y usar ese valor como estado inicial. Si no existe, asignar un valor por defecto seguro.

javascript const localStorageTodos = localStorage.getItem('TODOS_V1'); let parsedTodos;

if (!localStorageTodos) { localStorage.setItem('TODOS_V1', JSON.stringify([])); parsedTodos = []; } else { parsedTodos = JSON.parse(localStorageTodos); }

const [todos, setTodos] = React.useState(parsedTodos);

Este patrón usa la metodología error first: programa primero el caso menos ideal [15:40]. Si localStorage devuelve null, undefined o cualquier valor falsy, inicializas con un array vacío y lo guardas también en localStorage. Así React nunca recibe algo que no pueda iterar.

Fíjate en el detalle del nombre de la clave: usar TODOS_V1 con sufijo de versión te permite migrar la estructura de datos en el futuro sin romper la app de los usuarios actuales.

Cómo actualizar estado y localStorage al mismo tiempo

Cada vez que el usuario completa o elimina un todo, necesitas actualizar dos lugares: el estado de React y localStorage. En lugar de duplicar esa lógica, encapsúlala en una función única.

javascript const saveTodos = (newTodos) => { const stringifiedTodos = JSON.stringify(newTodos); localStorage.setItem('TODOS_V1', stringifiedTodos); setTodos(newTodos); };

Luego reemplazas todas las llamadas a setTodos por saveTodos dentro de funciones como completeTodo o deleteTodo. Con eso, cualquier cambio se refleja tanto en pantalla como en almacenamiento persistente.

Errores comunes al trabajar con localStorage

Durante el desarrollo es muy frecuente romper algo, y la causa casi siempre es la misma: datos mal formateados que ya quedaron guardados.

  • Guardar sin stringify: aparece "[object Object]" y JSON.parse falla con Unexpected token o in JSON.
  • Olvidar parsear al leer: el estado recibe un string en lugar de un array y se rompe .filter o .map.
  • No manejar el caso null: la primera vez que un usuario entra, no existe la clave y la app se rompe si no hay valor por defecto.

La solución rápida cuando algo se ensucia es ir a la consola y ejecutar localStorage.removeItem('TODOS_V1') para empezar limpio.

Por qué conviene mover esta lógica a un custom hook

Después de implementar todo esto, el componente principal queda con demasiada responsabilidad: maneja estado, lógica de negocio y persistencia. Esa mezcla hace que el código sea más difícil de leer y mantener.

La siguiente evolución natural es extraer toda la lógica de localStorage a un custom hook reutilizable, algo así como useLocalStorage. Eso permite limpiar el componente, reutilizar la persistencia en otras partes de la app y separar claramente las preocupaciones.

¿Cómo nombraste tú la función equivalente a saveTodos en tu proyecto? Cuéntalo en los comentarios.