📌  相关文章
📜  如何在没有任何外部库的情况下将 Redux 状态持久保存在本地存储中?

📅  最后修改于: 2022-05-13 01:56:36.953000             🧑  作者: Mango

如何在没有任何外部库的情况下将 Redux 状态持久保存在本地存储中?

根据官方文档,Redux 是 JavaScript 应用程序的可预测状态容器。简单来说就是一个状态管理库,借助 Redux 管理组件的状态变得非常容易。我们可以通过创建一个称为商店的全局状态来管理应用程序的状态。

使用 Redux 的想法可能适用于复杂的 react 应用程序,但这种状态并非始终保持不变。这意味着一旦您重新加载浏览器,应用程序的状态就会发生变化并达到其默认状态。持久化此类 React 应用程序的数据非常容易。我们将使用本地存储来存储 React 应用程序的当前状态,并在重新加载时持久保存数据。

创建 React 应用程序并安装模块:

第 1 步:使用以下命令创建一个 React 应用程序:

npx create-react-app myapp

第 2 步:创建项目文件夹(即myapp)后,使用以下命令移动到该文件夹:

cd myapp

第 3 步:创建 ReactJS 应用程序后,使用以下命令安装所需的模块:

npm install redux
npm install react-redux

项目结构:它将如下所示。

项目结构

示例:我们将创建一个简单的购物车应用程序,通过它我们将数据保存到本地存储中。

Filename- App.js这是我们的 React 应用程序的 App 组件。导入典型反应应用程序所需的所有依赖项。 provider函数是从 react-redux 导入的。这将作为我们 App 的包装组件,我们将通过 store 传递给这个包装组件。 store 基本上是我们应用程序的全局状态。

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 (
    
      
    
  );
}
  
export default App;


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


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;


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 (
    
      {img}       
        

{title}

        

${price}

         
      
        {/* increase amount */}                  {/* amount */}         

{amount}

           {/* decrease amount */}                
    
  ); };    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);


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 (
      
           
          

your bag

          

              is currently empty           

        
      
    );   }   return (     
      {/* cart header */}       
        

your bag

      
      {/* cart items */}       
        {cart.map((item) => {           return         })}       
      {/* cart footer */}       
        
        
          

            total ${total}           

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


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


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


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


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


Filename-store.js在这个文件中,基本的 store 设置是使用 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这个文件包含reducer函数。根据通过 UI 调度的操作,会发生相应的功能。主要是我们的 reducer 将处理我们应用程序的 5 个基本操作,即:

  • DECREASE动作减少了我们购物车中的商品数量。
  • INCREASE动作增加了我们购物车中商品的数量。
  • REMOVE操作从购物车中删除一个项目。
  • 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;
}
  
export default reducer;

文件名:CartItem.js此文件包含cartItem组件的代码。正是在这个文件中调度了不同的方法。函数mapDispatchToProps包括三个动作 DECREASE、INCREASE、REMOVE。

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 (
    
      {img}       
        

{title}

        

${price}

         
      
        {/* increase amount */}                  {/* amount */}         

{amount}

           {/* decrease amount */}                
    
  ); };    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);

Filename- CartContainer.js此文件导入 carttem 组件并将所需的道具传递给它们。这里我们连接mapStateToPropsmapDispatchToProps ,然后将 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 (
      
           
          

your bag

          

              is currently empty           

        
      
    );   }   return (     
      {/* cart header */}       
        

your bag

      
      {/* cart items */}       
        {cart.map((item) => {           return         })}       
      {/* cart footer */}       
        
        
          

            total ${total}           

        
               
    
  ); };    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 的文件,通常位于您的 react 应用程序的src文件夹中。在这个文件中,我们将添加两种方法,一种是从本地存储加载状态,另一种是将状态保存到本地存储。 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”,值是应用程序的实际状态。

第二步: saveState方法的代码如下:

Javascript

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

第 3 步:现在在store.js文件中,从localStorage.js文件中导入loadState方法,并在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文件中导入 store,并从localStorage.js文件中导入saveState()方法。现在通过从商店调用订阅函数来保存应用程序的状态。

文件名-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
  });
});

现在只需将这个 store 传递给 App 组件中的 Provider 组件,数据就会在本地存储中持久化。

输出:您可以通过在 chrome 上打开开发人员工具然后导航到应用程序然后导航到存储而不是本地存储来检查状态。在本地存储中,您将看到名为appState的键。这是存储整个数据的地方。