Как сохранить состояние Redux в локальном хранилище без какой-либо внешней библиотеки?

Опубликовано: 4 Января, 2022

Redux согласно официальной документации - это контейнер с предсказуемым состоянием для приложений JavaScript. Проще говоря, это библиотека управления состоянием, с помощью Redux управление состоянием компонентов становится очень простым. Мы можем управлять состоянием приложения, создавая глобальное состояние, известное как хранилище.

Идея использования Redux может быть хорошей для приложения со сложной реакцией, но это состояние не сохраняется на всем протяжении. Это означает, что после перезагрузки браузера состояние приложения изменяется и достигает состояния по умолчанию. Сохранять данные таких реагирующих приложений очень просто. Мы будем использовать локальное хранилище для хранения текущего состояния приложения React и сохранения данных даже при перезагрузках.

Создание приложения React и установка модуля:

Шаг 1. Создайте приложение React, используя следующую команду:

 npx создать-реагировать-приложение myapp

Шаг 2: После создания папки проекта, то есть myapp, перейдите к ней с помощью следующей команды:

cd myapp

Шаг 3: После создания приложения ReactJS установите необходимые модули, используя следующую команду:

 npm установить redux
npm установить React-redux

Структура проекта: это будет выглядеть следующим образом.

Структура проекта

Пример: мы создадим простое приложение корзины покупок, с помощью которого мы будем сохранять данные в нашем локальном хранилище.

Имя файла - App.js Это компонент нашего приложения React. Импортируются все зависимости, которые требуются для типичного приложения React. Функция провайдера импортируется из react-redux. Это будет действовать как компонент оболочки для нашего приложения, этому компоненту упаковки мы передадим хранилище. Магазин - это в основном глобальное состояние нашего приложения.

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 App; default

Filename- store.js В этом файле базовая настройка магазина выполняется с помощью redux. Хранилище инициализируется, и состояние сохраняется в локальном хранилище. Магазин redux содержит итоги, количество и элементы корзины. Состояние этого магазина будет позже сохранено в локальном хранилище.

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);

Имя файла: reducer.js Этот файл содержит функцию редуктора. В соответствии с действием, отправленным через пользовательский интерфейс, выполняются соответствующие функции. В основном наш редуктор будет иметь дело с 5 основными операциями нашего приложения, а именно:

  • Действие УМЕНЬШИТЬ уменьшает количество товаров в нашей корзине.
  • Действие УВЕЛИЧИТЬ увеличивает количество товаров в нашей корзине.
  • Действие УДАЛИТЬ удаляет товар из корзины.
  • Действие CLEAR_CART в основном очищает всю корзину.
  • Действие GET_TOTALS получает сумму всех товаров в нашей корзине.

Примечание. Следует помнить одну важную вещь: не изменяйте состояние приложения при использовании 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;
}
reducer; export default

Имя файла: CartItem.js Этот файл содержит код для компонента cartItem Именно в этом файле отправляются разные методы. Функция mapDispatchToProps включает три действия mapDispatchToProps

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 connect( default null , mapDispatchToProps)(CartItem);

Имя файла - CartContainer.js Этот файл импортирует компонент carttem и передает им необходимые реквизиты. Здесь мы подключаем mapStateToProps и mapDispatchToProps а затем передаем ему 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);

Имя файла- localStorage.js Теперь мы добавим файл localStorage.js. Метод сохранения данных требует всего четырех простых шагов:

Шаг 1. Создайте файл с именем localStorage.js в корневой папке, обычно в папке src вашего приложения React. В этом файле мы добавим два метода: один для загрузки состояния из локального хранилища, а второй - для сохранения состояния в локальное хранилище. Код для метода loadState выглядит следующим образом:

Javascript

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

В локальном хранилище данные хранятся в виде пар ключ-значение. Здесь ключом является appState, а значением будет фактическое состояние приложения.

Шаг 2: Код для saveState выглядит следующим образом:

Javascript

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

Шаг 3. Теперь в файле store.js импортируйте метод loadState из файла localStorage.js и получите его значение в константе persistedState Теперь в качестве объекта поместите эту persistedState с фактическим состоянием вашего приложения и экспортируйте ее, передав ее в хранилище.

Имя файла- 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);

Шаг 4: Это самый важный шаг, поскольку он включает в себя сохранение состояния в локальном хранилище браузера. Теперь в компоненте импорта App.js магазин из файла store.js и метод saveState () из файла localStorage.js. Теперь сохраните состояние приложения, вызвав функцию подписки из магазина.

Имя файла - 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
});
});

Теперь просто передайте это хранилище компоненту Provider в компоненте App, и данные станут постоянными в локальном хранилище.

Вывод: вы можете проверить состояние, открыв инструменты разработчика в Chrome, а затем перейдя в приложение, затем в хранилище, а затем в локальное хранилище. В локальном хранилище вы увидите ключ с именем appState . Это место, где хранятся все данные.