• redux 持久化(redux-persist) 结合 immutable 使用问题


    前言

    最近学习了redux以及react-redux的结合使用确实让redux在react中更好的输出代码啦~
    但是考虑到项目的各种需求,我们还是需要对redux进行深一步的改造,让其能更好的满足我们的日常开发,大大提高我们的开发效率。
    今天给大家推荐两个好用的功能包,并解决一个它们结合使用存在的问题。

    redux-persist

    redux-persist 主要用于帮助我们实现redux的状态持久化
    所谓状态持久化就是将状态与本地存储联系起来,达到刷新或者关闭重新打开后依然能得到保存的状态。

    安装

    yarn add redux-persist 
    // 或者
    npm i redux-persist
    
    • 1
    • 2
    • 3

    Github 地址

    https://github.com/rt2zz/redux-persist
    
    • 1

    大家可以去看看官方的说明文档,这里就不一一介绍功能了,简单讲一点常用功能和导入到项目使用。

    使用到项目上

    store.js

    带有 // ** 标识注释的就是需要安装后添加进去使用的一些配置,大家好好对比下投掷哦
    下面文件也是一样

    import { createStore, applyMiddleware, compose } from "redux";
    import thunk from 'redux-thunk'
    import { persistStore, persistReducer } from 'redux-persist' // **
    import storage from 'redux-persist/lib/storage' // **
    import reducer from './reducer'
    
    const persistConfig = {  // **
        key: 'root',// 储存的标识名
        storage, // 储存方式
        whitelist: ['persistReducer'] //白名单 模块参与缓存
    }
    
    const persistedReducer = persistReducer(persistConfig, reducer) // **
    
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    const store = createStore(persistedReducer, composeEnhancers(applyMiddleware(thunk))) // **
    const persistor = persistStore(store) // **
    
    export { // **
        store,
        persistor
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    index.js

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import { Provider } from 'react-redux'
    import { BrowserRouter } from 'react-router-dom'
    import { PersistGate } from 'redux-persist/integration/react' // **
    
    import { store, persistor } from './store' // **
    import 'antd/dist/antd.min.css';
    import './index.css';
    import App from './App';
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      
         
        {/*  使用PersistGate //**   */}
          
            
              
            
          
        
      
    );
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    persist_reducer.js

    注意此时的模块是在白名单之内,这样persist_reducer的状态就会进行持久化处理了

    import { DECREMENT } from './constant'
    
    const defaultState = ({
        count: 1000,
        title: 'redux 持久化测试'
    })
    
    const reducer = (preState = defaultState, actions) => {
        const { type, count } = actions
        switch (type) {
            case DECREMENT:
                 return { ...preState, count: preState.count - count * 1 }
            default:
                return preState
        }
    }
    
    
    export default reducer
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这样就可以使用起来了,更多的配置可以看看上面Github的地址上的说明文档

    immutable

    immutable 主要配合我们redux的状态来使用,因为reducer必须保证是一个纯函数,所以我们当状态中有引用类型的值时我们可能进行浅拷贝来处理,或者遇到深层次的引用类型嵌套时我们采用深拷贝来处理。

    但是我们会觉得这样的处理确实稍微麻烦,而且我们若是采用简单的深拷贝 JSON.parse JSON.stringify 来处理也是不靠谱的,存在缺陷 就比如属性值为undefined 时会忽略该属性。

    所以 immutable 就是来帮我们解决这些问题,使用它修改后会到的一个新的引用地址,且它并不是完全复制的,它会尽可能的利用到未修改的引用地址来进行复用,比起传统的深拷贝性能确实好很多。

    这里就不多说了,想要了解更多可以看看下面的GitHub官网说明文档。

    安装

    npm install immutable
    // 或者
    yarn add immutable
    
    
    • 1
    • 2
    • 3
    • 4

    GitHub地址

    https://github.com/immutable-js/immutable-js
    
    • 1

    使用到项目上

    count_reducer.js

    import { INCREMENT } from './constant'
    import { Map } from 'immutable'
    // 简单的结构用Map就行 复杂使用fromJs 读取和设置都可以getIn setIn ...
    const defaultState = Map({ // **
        count: 0,
        title: '计算求和案例'
    })
    
    const reducer = (preState = defaultState, actions) => {
        const { type, count } = actions
        switch (type) {
            case INCREMENT:
                // return { ...preState, count: preState.count + count * 1 }
                return preState.set('count', preState.get('count') + count * 1) // **
            default:
                return preState
        }
    }
    
    export default reducer
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    读取和派发如下 :
    派发无需变化,就是取值时需要get

    函数组件
        const dispatch = useDispatch()
        const { count, title } = useSelector(state => ({
            count: state.countReducer.get("count"),
            title: state.countReducer.get("title")
        }), shallowEqual)
        const handleAdd = () => {
            const { value } = inputRef.current
            dispatch(incrementAction(value))
        }
        const handleAddAsync = () => {
            const { value } = inputRef.current
            dispatch(incrementAsyncAction(value, 2000))
        }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    类组件
    
    class RedexTest extends Component {
        // ....略
        render() {
            const { count, title } = this.props
            return (
                

    Redux-test:{title}

    count:{count}

    this.inputRef = r} />
    ) } } //使用connect()()创建并暴露一个Count的容器组件 export default connect( state => ({ count: state.countReducer.get('count'), title: state.countReducer.get('title') }), { incrementAdd: incrementAction, incrementAsyncAdd: incrementAsyncAction } )(RedexTest)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    这样就可以使用起来了,更多的配置可以看看上面Github的地址上的说明文档

    结合使用存在的问题

    结合使用有一个坑!!!
    是这样的,当我们使用了redux-persist 它会每次对我们的状态保存到本地并返回给我们,但是如果使用了immutable进行处理,把默认状态改成一种它内部定制Map结构,此时我们再传给 redux-persist,它倒是不挑食能解析,但是它返回的结构变了,不再是之前那个Map结构了而是普通的对象,所以此时我们再在reducer操作它时就报错了,如下案例:

    组件

    import React, { memo } from "react";
    import { useDispatch, useSelector, shallowEqual } from "react-redux";
    import { incrementAdd } from "../store/persist_action";
    
    const ReduxPersist = memo(() => {
      const dispatch = useDispatch();
      const { count, title } = useSelector(
        ({ persistReducer }) => ({
          count: persistReducer.get("count"),
          title: persistReducer.get("title"),
        }),
        shallowEqual
      );
      return (
        

    ReduxPersist----{title}

    count:{count}

    ); }); export default ReduxPersist;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    persist-reducer.js

    import { DECREMENT } from './constant'
    import { fromJS } from 'immutable'
    
    const defaultState = fromJS({
        count: 1000,
        title: 'redux 持久化测试'
    })
    
    const reducer = (preState = defaultState, actions) => {
        const { type, count } = actions
        switch (type) {
            case DECREMENT:
                return preState.set('count', preState.get('count') - count * 1)
            default:
                return preState
        }
    }
    
    
    export default reducer
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    按理说是正常显示,但是呢由于该reducer是被redux-persist处理的,所以呢就报错了

    在这里插入图片描述

    报错提示我们没有这个 get 方法了,即表示变成了普通对象

    解决

    组件

    import React, { memo } from "react";
    import { useDispatch, useSelector, shallowEqual } from "react-redux";
    import { incrementAdd } from "../store/persist_action";
    
    const ReduxPersist = memo(() => {
      const dispatch = useDispatch();
      // **
      const { count, title } = useSelector(
        ({ persistReducer: { count, title } }) => ({
          count,
          title,
        }),
        shallowEqual
      );
      
      //const { count, title } = useSelector(
      //  ({ persistReducer }) => ({
      //   count: persistReducer.get("count"),
      //    title: persistReducer.get("title"),
      //  }),
      //  shallowEqual
      // );
      
      return (
        

    ReduxPersist----{title}

    count:{count}

    ); }); export default ReduxPersist;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    persist-reducer.js

    import { DECREMENT } from './constant'
    import { fromJS } from 'immutable'
    
    const defaultState = ({ // **
        count: 1000,
        title: 'redux 持久化测试'
    })
    
    const reducer = (preState = defaultState, actions) => {
        const { type, count } = actions
        let mapObj = fromJS(preState) // **
        switch (type) {
            case DECREMENT:
                // return preState.set('count', preState.get('count') - count * 1) 
                return mapObj.set('count', mapObj.get('count') - count * 1).toJS() // **
            default:
                return preState
        }
    }
    
    
    export default reducer
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    解决思路

    由于 redux-persist 处理每次会返回普通对象,所以我们只能等要在reducer中处理状态时,我们先将其用immutable处理成它内部定制Map结构,然后我们再进行set操作修改,最后我们又将Map结构转换为普通对象输出,这样就完美的解决了这个问题。

    End

    点赞,关注,收藏,不迷路!
    在这里插入图片描述

  • 相关阅读:
    git如何删除github上的文件,亲测有效
    任意长度循环卷积&单位根反演 学习笔记
    基于MiDas的深度估计算法移植与测试
    获取淘宝商品详情API、商品主图、图片搜索api
    图的存储 —— 邻接矩阵
    配置之别名优化,映射器优化 P7,P8
    [面试直通版]网络协议面试核心之HTTP,HTTPS,DNS-TLS技术
    【Netty 几个组件介绍】
    《Effective Java》知识点(3)--类和接口
    python继承
  • 原文地址:https://blog.csdn.net/kzj0916/article/details/126212767