React-Native 数据持久化

  • 使用RN也有一段时间了,但是却未试过在RN项目中实现数据的持久化,虽然项目继承了react-redux,但是数据存在于内存中,用户关闭APP后,需要重新初始化数据,网上大多RN数据持久化都是将一些数据存在AsyncStorage中,但是却打断了与redux的联系
  • Github上已经有现成的redux-persist包以解决redux持久化问题,但在实际使用过程中,还有很多问题需要解决。具体来说,redux-persist这个包提供的是通用解决方案,也可以用于react.js,如果你要用在react-native中的话,需要指定AsyncStorage,另外,虽然它还额外提供了两个transform插件redux-persist-transform-immutableredux-persist-immutable,但这两个插件目前使用起来还是有问题没有解决,为了尽快用上redux-persist,可以使用以下方案。
解决

在建立redux store时,除了常规会用到的各种中间件以外,我们需要额外引入redux-persist里的autoRehydrate增强器,然后启动持久化。

Store.ts
import { applyMiddleware, createStore, compose } from 'redux';
import { autoRehydrate } from 'redux-persist';
import createSagaMiddleware from 'redux-saga';
// 中间件,作用:如果不使用该中间件,当我们dispatch一个action时,需要给dispatch函数传入action对象;
// 但如果我们使用了这个中间件,那么就可以传入一个函数,这个函数接收两个参数:dispatch和getState。这个dispatch可以在将来的异步请求完成后使用,对于异步action很有用
import thunk from 'redux-thunk';
import logger from 'redux-logger';
// 引入reducer
import reducer from './reducer';
// 引入持久化配置文件
import ReduxPersist from '../config/ReduxPersist';
// 引入版本变化重新持久化文件
import RehydrationServices from '../config/RehydrationServices';
// 使用redux-thunk中间件,处理异步action,这里则不实用saga中间件
// const sagaMiddleware = createSagaMiddleware();
// let middleware:any = [];
// middleware.push(sagaMiddleware);

let store: any = {};
const createAppropriateStore =  createStore;

if (ReduxPersist.active) {
    // 如果配置中要求采用持久化
    const enhancers = compose(
      applyMiddleware(thunk, logger), // 加入thunk中间件和日志中间件
      autoRehydrate()
    );

    store = createAppropriateStore(
        reducer,
        enhancers
    );
    // 启动持久化
    RehydrationServices.updateReducers(store);
} else {
    // 如果配置中不要求采用持久化
    store = createStore(
        reducer,
        applyMiddleware(thunk, logger)
    );
}

export default store;

ReduxPersist.ts 持久化配置文件
import { AsyncStorage } from 'react-native';

import immutablePersistenceTransform from '../redux/ImmutablePersistenceTransform';
import { persistentStoreBlacklist } from '../redux/reducer';

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

export default REDUX_PERSIST;
RehydrationServices.ts 判断是否替换持久化数据文件
import { AsyncStorage } from 'react-native';
import { persistStore } from 'redux-persist';

import ReduxPersist from './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};
reducer/index.ts 合并所有reducers和导出黑名单
// 工具函数,用于组织多个reducer,并返回reducer集合
import { combineReducers } from 'redux';

import configReducer from './configReducer';
import reduxPersister from './reduxPersister'
let reducers = {
    config:configReducer,
    reduxPersister,
};
// 导出所有reducer
export default combineReducers(reducers);
// 添加persist黑名单,以下这些reducer不需要持久化
export const persistentStoreBlacklist = [
    'config',
  ];
ImmutablePersistenceTransform.ts 数据合并和转换文件
import R from 'ramda';
import Immutable from 'seamless-immutable';

// 将redux中的immutable对象转为普通js对象,以便于持久化存储
const isImmutable = R.has('asMutable');
const convertToJs = (state: any) => state.asMutable({deep: true});
const fromImmutable = R.when(isImmutable, convertToJs);

// 将普通js对象转为immutable不可变,以供redux使用
const toImmutable = (raw: any) => Immutable(raw);

export default {
  out: (state: any) => {
    // 设置深度合并
    state.mergeDeep = R.identity;
    // 从仓库中取出,进入内存时,转为immutable不可变
    return toImmutable(state);
  },
  in: (raw: any) => {
    // 进入仓库时,将immutable不可变数据转为常规数据
    return fromImmutable(raw);
  }
};
action.ts 异步action文件
import * as actionTypes from './actionTypes'
export function DemoAction(params:boolean) {
    return (dispatch:any, getState:any) => {
        dispatch({
            type: actionTypes.SOMETEST,
            data: params
        });
    };
};
configReducer.ts
import * as actionTypes from '../action/actionTypes';

const initialState = {
    someData: ''
};

export default (state = initialState, action:any) => {
    switch (action.type) {
        case actionTypes.SOMETEST:
            return {
                ...state,
                someData: action.data
            }

        default:
            return state
    }
};

参考文章:

张京《在React Native中将Redux数据持久化》

点赞