¿Cómo persistir el estado de Redux en el almacenamiento local sin ninguna biblioteca externa?

Redux según la documentación oficial es un contenedor de estado predecible para aplicaciones de JavaScript. En palabras simples, es una biblioteca de administración de estado, con la ayuda de Redux, administrar el estado de los componentes se vuelve muy fácil. Podemos administrar el estado de la aplicación creando un estado global conocido como tienda.

La idea de usar Redux puede estar bien para una aplicación de reacción compleja, pero este estado no es persistente en todo momento. Significa que una vez que recarga el navegador, el estado de la aplicación cambia y alcanza su estado predeterminado. Conservar los datos de tales aplicaciones de reacción es muy fácil. Usaremos el almacenamiento local para almacenar el estado actual de la aplicación React y conservar los datos incluso en las recargas. 

Creación de la aplicación React e instalación del módulo:

Paso 1: Cree una aplicación React usando el siguiente comando:

npx create-react-app myapp

Paso 2: después de crear la carpeta de su proyecto, es decir , myapp, acceda a ella con el siguiente comando:

cd myapp

Paso 3: después de crear la aplicación ReactJS, instale los módulos necesarios con el siguiente comando:

npm install redux
npm install react-redux

Estructura del proyecto: Tendrá el siguiente aspecto.

Estructura del proyecto

Ejemplo: crearemos una aplicación de carrito de compras simple a través de la cual conservaremos los datos en nuestro almacenamiento local.

Filename- App.js Este es el componente de la aplicación de nuestra aplicación React. Se importan todas las dependencias que se requieren para una aplicación de reacción típica. La función de proveedor se importa de react-redux. Esto actuará como un componente de envoltura para nuestra aplicación, a este componente de envoltura le pasaremos la tienda. La tienda es básicamente un estado global de nuestra aplicación.

Javascript

import React from "react";
import { Provider } from 'react-redux';
import CartContainer from "./components/CartContainer";
 
// Store
import { store } from './store';
import { saveState } from './localStorage';
 
store.subscribe(() => {
  saveState({
    cart: store.getState().cart,
    total: store.getState().total,
    amount: store.getState().amount
  });
});
 
// Items
const cartItems = [
  {
    id: 1,
    title: "Samsung",
    price: 799.99,
    img:
      "shorturl.at/ajkq9",
    amount: 1
  },
  {
    id: 2,
    title: "Google pixel Max",
    price: 399.99,
    img:
      "shorturl.at/ajkq9",
    amount: 1
  },
  {
    id: 3,
    title: "Xiaomi",
    price: 999.99,
    img:
      "shorturl.at/ajkq9",
    amount: 1
  }
];
 
function App() {
  return (
    <Provider store={store}>
      <CartContainer cart={cartItems} />
    </Provider>
  );
}
 
export default App;

Filename- store.js En este archivo, la configuración básica de la tienda se realiza mediante redux. El almacén se inicializa y el estado se mantiene en el almacenamiento local. La tienda redux contiene el total, la cantidad y los artículos del carrito. El estado de esta tienda se guardará más tarde en el almacenamiento local.

Javascript

import reducer from './reducer';
import { createStore } from 'redux';
import { loadState } from './localStorage';
 
const cartItems = [
  {
    id: 1,
    title: "Samsung",
    price: 799.99,
    img:
      "shorturl.at/ajkq9",
    amount: 1
  },
  {
    id: 2,
    title: "Google pixel Max",
    price: 399.99,
    img:
      "shorturl.at/ajkq9",
    amount: 1
  },
  {
    id: 3,
    title: "Xiaomi",
    price: 999.99,
    img:
      "shorturl.at/ajkq9",
    amount: 1
  }
];
 
const persistedState = loadState();
 
const initialStore = {
  cart: cartItems,
  amount: 0,
  total: 0,
  persistedState
}
 
export const store = createStore(reducer, persistedState);

Nombre de archivo: reducer.js Este archivo contiene la función de reducción. Según la acción enviada a través de la interfaz de usuario, se lleva a cabo la funcionalidad correspondiente. Principalmente, nuestro reductor se ocupará de 5 operaciones básicas de nuestra aplicación, a saber:

  • La acción DISMINUIR disminuye la cantidad de artículos en nuestro carrito.
  • La acción AUMENTAR aumenta la cantidad de artículos en nuestro carrito.
  • La acción ELIMINAR elimina un artículo del carrito.
  • La acción CLEAR_CART básicamente borra todo el carrito.
  • La acción GET_TOTALS obtiene la suma de todos los artículos en nuestro carrito.

Nota: Una cosa importante para recordar es que no cambie el estado de la aplicación mientras usa Redux. 

Javascript

import {
  INCREASE,
  DECREASE,
  REMOVE,
  CLEAR_CART,
  GET_TOTALS,
} from './actions';
 
function reducer(state, action) {
  if (action.type === DECREASE) {
    return {
      ...state, cart: state.cart.map((item) => {
        if (item.id === action.payload.id) {
          if (item.amount === 0) {
            return item;
          } else {
            item.amount--;
          }
        }
        return item;
      })
    }
  }
  if (action.type === INCREASE) {
    return {
      ...state, cart: state.cart.map((item) => {
        if (item.id === action.payload.id) {
          item.amount++;
        }
        return item;
      })
    }
  }
  if (action.type === CLEAR_CART) {
    return { ...state, cart: [] };
  }
  if (action.type === REMOVE) {
    return {...state, cart: state.cart.filter(item => item.id !== action.payload.id)}
  }
  if (action.type === GET_TOTALS) {
    let { total, amount } = state.cart.reduce((cartTotal, cartItem) => {
      const { price, amount } = cartItem;
      cartTotal.amount += amount;
      cartTotal.total += Math.floor(amount * price);
      return cartTotal;
    }, { amount: 0, total: 0 });
    return { ...state, total, amount };
  }
  return state;
}
 
export default reducer;

Nombre de archivo: CartItem.js Este archivo contiene el código para el componente cartItem. Es en este archivo que se despachan diferentes métodos. La función mapDispatchToProps incluye tres acciones DISMINUIR, AUMENTAR, ELIMINAR.

Javascript

import React from "react";
import { connect } from 'react-redux';
import { DECREASE, INCREASE, REMOVE } from '../actions';
 
const CartItem = ({ title, price, img, amount, increase, decrease, remove }) => {
  return (
    <div className="cart-item">
      <img src={img} alt={img} />
      <div>
        <h4>{title}</h4>
        <h4 className="item-price">${price}</h4>
 
      </div>
      <div>
        {/* increase amount */}
        <button className="amount-btn"
          onClick={() => increase()}>
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
            <path
      d="M10.707 7.05L10 6.343 4.343 12l1.414 1.414L10 9.172l4.243 4.242L15.657 12z"
            />
          </svg>
        </button>
        {/* amount */}
        <p className="amount">{amount}</p>
 
 
        {/* decrease amount */}
        <button className="amount-btn"
          onClick={() => decrease()} >
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
            <path
      d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"
            />
          </svg>
        </button>
      </div>
    </div>
  );
};
 
const mapDispatchToProps = (dispatch, ownProps) => {
  const { id, amount } = ownProps;
  return {
    increase: () => dispatch({ type: INCREASE, payload: { id } }),
    decrease: () => dispatch({ type: DECREASE, payload: { id } }),
    remove: () => dispatch({ type: REMOVE, payload: { id, amount } })
  }
}
 
export default connect(null, mapDispatchToProps)(CartItem);

Nombre de archivo: CartContainer.js Este archivo importa el componente CartItem y les pasa los accesorios que se necesitan. Aquí conectamos mapStateToProps y mapDispatchToProps y luego le pasamos CartContainer.

Javascript

import React from "react";
import CartItem from './CartItem';
import { connect } from 'react-redux';
import { CLEAR_CART, GET_TOTALS } from '../actions';
 
const CartContainer = ({ cart = [], total, remove, getTotal }) => {
  React.useEffect(() => {
    getTotal();
  })
  if (cart.length === 0) {
    return (
      <section className="cart">
 
        <header>
          <h2>your bag</h2>
          <h4 className="empty-cart">
              is currently empty
          </h4>
        </header>
      </section>
    );
  }
  return (
    <section className="cart">
      {/* cart header */}
      <header>
        <h2>your bag</h2>
      </header>
      {/* cart items */}
      <article>
        {cart.map((item) => {
          return <CartItem key={item.id} {...item} />
        })}
      </article>
      {/* cart footer */}
      <footer>
        <hr />
        <div className="cart-total">
          <h4>
            total <span>${total}</span>
          </h4>
        </div>
        <button className="btn clear-btn"
          onClick={() => remove()} >clear cart</button>
      </footer>
    </section>
  );
};
 
function mapStateToProps(store) {
  const { cart, total } = store;
  return { cart: cart, total: total };
}
 
function mapDispatchToProps(dispatch) {
  return {
    remove: () => dispatch({ type: CLEAR_CART }),
    getTotal: () => dispatch({ type: GET_TOTALS })
  }
}
 
export default connect(mapStateToProps, mapDispatchToProps)(CartContainer);

Nombre de archivo: localStorage.js Ahora agregaremos el archivo localStorage.js . El método de persistencia de datos requiere solo cuatro pasos simples:

Paso 1: Cree un archivo llamado localStorage.js en la carpeta raíz, normalmente en la carpeta src de su aplicación de reacción. En este archivo, agregaremos dos métodos, uno para cargar el estado desde el almacenamiento local y el otro método para guardar el estado en el almacenamiento local. El código para el método loadState es el siguiente:

Javascript

export const loadState = () => {
    try {
      const serialState = localStorage.getItem('appState');
      if (serialState === null) {
        return undefined;
      }
      return JSON.parse(serialState);
    } catch (err) {
      return undefined;
    }
};

En el almacenamiento local, los datos se almacenan en forma de pares clave-valor. Aquí la clave es ‘appState’ y el valor será el estado real de la aplicación.

Paso 2: el código para el método saveState es el siguiente:

Javascript

export const saveState = (state) => {
    try {
      const serialState = JSON.stringify(state);
      localStorage.setItem('appState', serialState);
    } catch(err) {
        console.log(err);
    }
};

Paso 3: ahora, en el archivo store.js , importe el método loadState del archivo localStorage.js y obtenga su valor en la constante persistedState. Ahora, como un objeto, coloque esta constante de estado persistente con el estado real de su aplicación y expórtela pasándola a la tienda.

Nombre de archivo- store.js

Javascript

import reducer from './reducer';
import {createStore} from 'redux';
import {loadState} from './localStorage';
 
const persistedState = loadState();
 
const initialStore={
    /* state of your app */
    cart:cartItems,
    amount:0,
    total:0,
    persistedState
}
    
export const store=createStore(reducer,persistedState);

Paso 4: Este es el paso más importante ya que implica guardar el estado en el almacenamiento local del navegador. Ahora, en el componente App.js, importe la tienda desde el archivo store.js y el método saveState() desde el archivo localStorage.js . Ahora guarde el estado de la aplicación llamando a la función de suscripción desde la tienda.

Nombre de archivo- App.js

Javascript

import {store} from './store';
import {saveState} from './localStorage';
 
store.subscribe(() => {
  saveState({
   /* example state */
    cart:store.getState().cart,
    total:store.getState().total,
    amount: store.getState().amount
  });
});

Ahora simplemente pase esta tienda al componente Proveedor en el componente Aplicación y los datos se volverán persistentes en el almacenamiento local. 

Salida: puede verificar el estado abriendo las herramientas de desarrollo en Chrome y luego navegando a Aplicación, luego a Almacenamiento que al almacenamiento local. En el almacenamiento local, verá la clave denominada appState . Este es el lugar donde se almacenan todos los datos.

Publicación traducida automáticamente

Artículo escrito por niteshkrs y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *