- 使用RN也有一段時間了,但是卻未試過在RN項目中實現(xiàn)數(shù)據(jù)的持久化,雖然項目繼承了
react-redux,但是數(shù)據(jù)存在于內(nèi)存中,用戶關閉APP后,需要重新初始化數(shù)據(jù),網(wǎng)上大多RN數(shù)據(jù)持久化都是將一些數(shù)據(jù)存在AsyncStorage中,但是卻打斷了與redux的聯(lián)系 -
Github上已經(jīng)有現(xiàn)成的redux-persist包以解決redux持久化問題,但在實際使用過程中,還有很多問題需要解決。具體來說,redux-persist這個包提供的是通用解決方案,也可以用于react.js,如果你要用在react-native中的話,需要指定AsyncStorage,另外,雖然它還額外提供了兩個transform插件redux-persist-transform-immutable和redux-persist-immutable,但這兩個插件目前使用起來還是有問題沒有解決,為了盡快用上redux-persist,可以使用以下方案。
解決
在建立redux store時,除了常規(guī)會用到的各種中間件以外,我們需要額外引入redux-persist里的autoRehydrate增強器,然后啟動持久化。
Store.ts
import { applyMiddleware, createStore, compose } from 'redux';
import { autoRehydrate } from 'redux-persist';
import createSagaMiddleware from 'redux-saga';
// 中間件,作用:如果不使用該中間件,當我們dispatch一個action時,需要給dispatch函數(shù)傳入action對象;
// 但如果我們使用了這個中間件,那么就可以傳入一個函數(shù),這個函數(shù)接收兩個參數(shù):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不可變的,此處必須將常規(guī)數(shù)據(jù)做變形,否則會失敗
}
};
export default REDUX_PERSIST;
RehydrationServices.ts 判斷是否替換持久化數(shù)據(jù)文件
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版本不同,則需要清理持久化數(shù)據(jù)
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和導出黑名單
// 工具函數(shù),用于組織多個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 數(shù)據(jù)合并和轉換文件
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;
// 從倉庫中取出,進入內(nèi)存時,轉為immutable不可變
return toImmutable(state);
},
in: (raw: any) => {
// 進入倉庫時,將immutable不可變數(shù)據(jù)轉為常規(guī)數(shù)據(jù)
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
}
};