在开发react-native
过程当中,使用redux
保存状态迁移已基本成为一个标准作法。用户登陆时的状态变动,会带来redux
状态迁移,而应用程序的其余部分也须要了解用户是否已登陆以及相关的登陆信息,只要软件不退出,经过reducer
咱们老是能感知到变化的。但问题是软件退出后,reducer
从内存中消失,用户若是再次打开软件,还须要登陆。简单作法是把登陆的token等信息存储在react-native
提供的AsyncStorage
里,但这样一来就打断了和redux
的联系。有没有可能直接把redux
的信息保存在AsyncStorage
里呢?这样一来咱们就既解决了记住用户登陆信息的问题,同时又不打破redux
的优良结构。react
Github
上已经有现成的redux-persist包以解决redux
持久化问题,但在实际使用过程当中,还有不少问题须要解决。具体来讲,redux-persist
这个包提供的是通用解决方案,也能够用于react.js
,若是你要用在react-native
中的话,须要指定AsyncStorage
,另外,虽然它还额外提供了两个transform
插件redux-persist-transform-immutable和redux-persist-immutable,但这两个插件目前使用起来仍是有问题没有解决,为了尽快用上redux-persist
,能够使用如下方案。git
首先,在创建redux store时,除了常规会用到的各类中间件之外,咱们须要额外引入redux-persist
里的autoRehydrate
加强器,而后启动持久化。这部分代码保存在Store
目录下的Store.js
文件中:github
// @flow import { createStore, applyMiddleware, compose } from 'redux'; import { autoRehydrate } from 'redux-persist'; import createSagaMiddleware from 'redux-saga'; import rootReducer from '../Reducers/'; import sagas from '../Sagas/'; import RehydrationServices from '../Services/RehydrationServices'; import ReduxPersist from '../Config/ReduxPersist'; import Config from '../Config/DebugConfig'; // 屏蔽flow误报警 declare var console: any; // 添加saga中间件 let middleware = []; const sagaMiddleware = createSagaMiddleware(); middleware.push(sagaMiddleware); export default () => { let store = {}; // 根据配置要求采用Reactotron或者原生store const createAppropriateStore = Config.useReactotron ? console.tron.createStore : createStore; if (ReduxPersist.active) { // 若是配置中要求采用持久化 const enhancers = compose( applyMiddleware(...middleware), autoRehydrate() ); store = createAppropriateStore( rootReducer, enhancers ); // 启动持久化 RehydrationServices.updateReducers(store); } else { // 若是配置中不要求采用持久化 const enhancers = compose( applyMiddleware(...middleware), ); store = createAppropriateStore( rootReducer, enhancers ); } // 运行saga sagaMiddleware.run(sagas); return store; };
代码中又对其余几段代码作了依赖,其中放在Reducers
目录下的index.js
中定义了黑名单,放在黑名单中的reducer
是不进行持久化的:redux
// @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
里:react-native
// @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
文件的配置:app
// @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
中:less
// @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
中取值。是否是很方便呢?异步
完整代码可参见我在Github上的项目:Wecanmobile。以为有帮助的话,请帮我打一颗星星。ide