zoukankan      html  css  js  c++  java
  • React-Native 中将 redux中的数据持久化

    问题:

    在开发react-native过程中,使用redux保存状态迁移已基本成为一个标准做法。用户登录时的状态变更,会带来redux状态迁移,而应用程序的其他部分也需要了解用户是否已登录以及相关的登录信息,只要软件不退出,通过reducer我们总是能感知到变化的。但问题是软件退出后,reducer从内存中消失,用户如果再次打开软件,还需要登录。简单做法是把登录的token等信息存储在react-native提供的AsyncStorage里,但这样一来就打断了和redux的联系。有没有可能直接把redux的信息保存在AsyncStorage里呢?这样一来我们就既解决了记住用户登录信息的问题,同时又不打破redux的优良结构。

    Github上已经有现成的redux-persist包以解决redux持久化问题,但在实际使用过程中,还有很多问题需要解决。具体来说,redux-persist这个包提供的是通用解决方案,也可以用于react.js,如果你要用在react-native中的话,需要指定AsyncStorage,另外,虽然它还额外提供了两个transform插件redux-persist-transform-immutableredux-persist-immutable,但这两个插件目前使用起来还是有问题没有解决,为了尽快用上redux-persist,可以使用以下方案。

    解决:

     1 // @flow
     2 
     3 import { createStore, applyMiddleware, compose } from 'redux';
     4 import { autoRehydrate } from 'redux-persist';
     5 import createSagaMiddleware from 'redux-saga';
     6 import rootReducer from '../Reducers/';
     7 import sagas from '../Sagas/';
     8 import RehydrationServices from '../Services/RehydrationServices';
     9 import ReduxPersist from '../Config/ReduxPersist';
    10 import Config from '../Config/DebugConfig';
    11 
    12 // 屏蔽flow误报警
    13 declare var console: any;
    14 
    15 // 添加saga中间件
    16 let middleware = [];
    17 const sagaMiddleware = createSagaMiddleware();
    18 middleware.push(sagaMiddleware);
    19 
    20 export default () => {
    21   let store = {};
    22 
    23   // 根据配置要求采用Reactotron或者原生store
    24   const createAppropriateStore = Config.useReactotron ? console.tron.createStore : createStore;
    25 
    26   if (ReduxPersist.active) {
    27     // 如果配置中要求采用持久化
    28     const enhancers = compose(
    29       applyMiddleware(...middleware),
    30       autoRehydrate()
    31     );
    32 
    33     store = createAppropriateStore(
    34       rootReducer,
    35       enhancers
    36     );
    37 
    38     // 启动持久化
    39     RehydrationServices.updateReducers(store);
    40   } else {
    41     // 如果配置中不要求采用持久化
    42     const enhancers = compose(
    43       applyMiddleware(...middleware),
    44     );
    45 
    46     store = createAppropriateStore(
    47       rootReducer,
    48       enhancers
    49     );
    50   }
    51 
    52   // 运行saga
    53   sagaMiddleware.run(sagas);
    54 
    55   return store;
    56 };

    代码中又对其他几段代码做了依赖,其中放在Reducers目录下的index.js中定义了黑名单,放在黑名单中的reducer是不进行持久化的:

    // @flow
    
    import { combineReducers } from 'redux';
    
    import LoginReducer from './LoginReducer';
    import ActivitiesReducer from './ActivitiesReducer';
    import ActivityReducer from './ActivityReducer';
    import ResourcesReducer from './ResourcesReducer';
    import NewsesReducer from './NewsesReducer';
    
    export default combineReducers({
      login: LoginReducer,
      activities: ActivitiesReducer,
      activity: ActivityReducer,
      resources: ResourcesReducer,
      newses: NewsesReducer,
    });
    
    // 添加persist黑名单,以下这些reducer不需要持久化
    export const persistentStoreBlacklist = [
      'activities',
      'activity',
      'resources',
      'newses',
    ];

    设置好黑名单之后,可以开始真正启用持久化了,这部分代码放在Services目录下的RehydrationServices.js里:

    // @flow
    
    import { AsyncStorage } from 'react-native';
    import { persistStore } from 'redux-persist';
    
    import ReduxPersist from '../Config/ReduxPersist';
    
    const updateReducers = (store: any) => {
      const reducerVersion = ReduxPersist.reducerVersion;
      const config = ReduxPersist.storeConfig;
    
      // 按照配置要求自动持久化reducer
      persistStore(store, config);
    
      AsyncStorage.getItem('reducerVersion').then((localVersion) => {
        // 从本地存储取出reducer版本并比较
        if (localVersion !== reducerVersion) {
          // 如果本地存储中的reducer版本与配置文件中的reducer版本不同,则需要清理持久化数据
          persistStore(store, config, () => {
            persistStore(store, config);
          }).purge([]);
          // 清理成功,将本地存储中的reducer版本设为配置文件中的reducer版本
          AsyncStorage.setItem('reducerVersion', reducerVersion);
        }
      }).catch(() => AsyncStorage.setItem('reducerVersion', reducerVersion));
    }
    
    export default {updateReducers};

    这里要取Config目录下的ReduxPersist.js文件的配置:

    // @flow
    
    import { AsyncStorage } from 'react-native';
    
    import immutablePersistenceTransform from '../Store/ImmutablePersistenceTransform';
    import { persistentStoreBlacklist } from '../Reducers/';
    
    const REDUX_PERSIST = {
      active: true, // 是否采用持久化策略
      reducerVersion: '2',  // reducer版本,如果版本不一致,将刷新整个持久化仓库
      storeConfig: {
        storage: AsyncStorage,  // 采用本地异步存储,react-native必须
        blacklist: persistentStoreBlacklist,  // 从根reducer获取黑名单,黑名单中的reducer不进行持久化保存
        transforms: [immutablePersistenceTransform],  // 重要,因为redux是immutable不可变的,此处必须将常规数据做变形,否则会失败
      }
    };
    
    export default REDUX_PERSIST;

    这里用到了一个最重要的变形,否则整个过程不能成功,因为redux里的对象都是immutable不可变的,我们在将它们持久化的时候,必须转成mutable可变的常规js对象,而从本地存储中取出来进入redux循环的时候,又需要将它们变成immutable的。下面这段代码要放在Store目录下的ImmutablePersistenceTransform.js中:

    // @flow
    
    import R from 'ramda';
    import Immutable from 'seamless-immutable';
    
    // 将redux中的immutable对象转为普通js对象,以便于持久化存储
    const isImmutable = R.has('asMutable');
    const convertToJs = (state) => state.asMutable({deep: true});
    const fromImmutable = R.when(isImmutable, convertToJs);
    
    // 将普通js对象转为immutable不可变,以供redux使用
    const toImmutable = (raw) => Immutable(raw);
    
    export default {
      out: (state: any) => {
        // 设置深度合并
        state.mergeDeep = R.identity;
        // 从仓库中取出,进入内存时,转为immutable不可变
        return toImmutable(state);
      },
      in: (raw: any) => {
        // 进入仓库时,将immutable不可变数据转为常规数据
        return fromImmutable(raw);
      }
    };

    和常规使用方法一样,原先如何使用redux,现在还是怎么样用,应用程序启动时,直接判断保存用户登录信息的reducer里有没有值就行了,如果没有的话,调出登录界面,如果有的话,直接从reducer中取值。是不是很方便呢?

  • 相关阅读:
    【ESXI6.0】 ESXI6.0安装时无法安装网卡驱动的解决方法及将网卡驱动加载进ISO
    [转]Vs解决方案的目录结构设置和管理
    在控制台编译运行java程序详细指导
    详解Linux安装GCC方法
    MySQL server has gone away 问题的解决方法
    eclipse或Myeclipse中web项目没有run on server时怎么办?
    DTM/DEM/DSM/DOM/DLG
    linux常用命令
    为什么很多地方看到初始值是1970年8月1日
    mongoDB
  • 原文地址:https://www.cnblogs.com/GGbondLearn/p/12252770.html
Copyright © 2011-2022 走看看