useState() persistente en React – ejemplo de un hook personalizado
Cuando creamos una aplicación en React podemos tener la información necesaria para trabajar en diferentes lugares. Por ejemplo, si usamos Redux, es frecuente tener el grueso de la información en uno o varios Stores. Pero, existe otro tipo de información que por su naturaleza nos puede resultar mas adecuado gestionarla usando React Context. Por ejemplo, el idioma por defecto del usuario o si quiere ver la aplicación con colores claros u oscuros. Si queremos que la información del contexto sea variable (manipulable) es necesario almacenarla en el estado de algún componente (normalmente el componente App). El problema es que los componentes pierden su estado si la página Web se recarga, y por lo tanto nuestro contexto variable también se pierde. En este artículo veremos como crear un useState() con persistencia usando un hook personalizado, de forma que al recargar la página su estado se recupere automáticamente.
React Context y el problema de la persistencia
React Context nos permite que cierta información sea accesible desde cualquier componente sin necesidad de pasar dicha información de padres a hijos mediante propiedades, ni nada parecido.
Además, dicha información puede ser modificable simplemente guardándola en el estado de nuestra aplicación. El problema es que si el usuario recarga la página pulsado F5 o dándole al botoncito de recargar, dicha información se pierde, y por lo tanto el contexto se reinicia.
Aunque es factible volver a cargar dicha información (por ejemplo, desde la base de datos del servidor), no deja de ser un fastidio que algo tan aparentemente intranscendente como recargar la página nos complique el diseño de nuestra aplicación React.
Todo esto se resolvería fácilmente si simplemente tuviéramos un useState() que fuera capaz de guardar la información sin perderla al recargarse la página.
Un hook personalizado para un useState() persistente
A continuación tienes el código de ejemplo de un sencillo hook personalizado que simplemente hace lo mismo que el useState(), pero guarda además la información en el localStorage del navegador del usuario, de forma que si se recarga la página, dicha información se recupera del localStorage automaticamente.
import { useState, useEffect } from "react"; export function useLocalStorageState(nombre, estado) { const [state, setData] = useState(estado); const obj = (typeof(estado) == "object"); const setState = (estado) => { setData(estado); if (obj) localStorage.setItem(nombre, JSON.stringify(estado)); else localStorage.setItem(nombre, estado); } useEffect(() => { let fromLS = localStorage.getItem(nombre); if (fromLS) { if (obj) setData(JSON.parse(fromLS)); else setData(fromLS); } }, [state]); return [ state, setState ]; }
Guarda este código en un archivo llamado, por ejemplo, useLocalStorageState.jsx, y después en tu App.js impórtalo:
import { useLocalStorageState } from './useLocalStorageState'
Y después tan solo tienes que usarlo en lugar de usar el useState() para crear el estado que quieres compartir en tu React Context. La única diferencia es que en este caso el primer parámetro es el nombre que quieres darle a la información ya que es el nombre con el que se guardará en el localStorage. El segundo parámetro es simplemente el estado deseado, al igual que sucede con useState().
const [idioma, setIdioma] = useLocalStorageState("idioma-usuario", "es");
Como el useState() que necesitamos pertenece a la aplicación, podemos estar seguros que va a ser un único componente el que haga uso de este hook. Si no es tu caso, y quieres usar este hook en varios componentes de tu aplicación, asegúrate que el primer parámetro es diferente para cada componente, para que los estados de los diferentes componentes se guarden con diferente nombre en el localStorage.
Y nada más, espero que esté código de ejemplo os resulte interesante.