在創(chuàng)建redux store 時(shí)屹耐, 將persistReducer
包裝應(yīng)用的rootReducer,然后傳遞給createStore
函數(shù)。一旦store創(chuàng)建完成有巧,將其傳遞給persistStore
函數(shù)隧膘,用來(lái)確保redux狀態(tài)當(dāng)發(fā)生變化時(shí)能夠持久化存儲(chǔ)
// src/store/index.js
import {createStore} from 'redux';
import {persistStore, persistReducer} from 'redux-persist';
import storage from 'reduc-persist/lib/storage';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import rootReducer from './reducers'; // 即combineReducers之后的rootReducer
const persistConfig = {
key: 'root',
storage: storage,
stateReconciler: autoMergeLevel2 // 查看 'Merge Process' 部分的具體情況
};
const persistReducer = persistReducer(persistConfig, rootReducer); // 包裝rootReducer
export const store = createStore(persistReducer); // 傳遞給createStore函數(shù) 這個(gè)export
export const persistor = persistStore(store); // 包裝store 這個(gè)也export
如果使用React,則使用 PersistGate
包裹根組建。這將延遲渲染app UI直到持久化狀態(tài)取回并保存到redux中
import React from 'react';
import {Provider} from 'react-redux';
import { PersistGate } from 'redux-persist/lib/integration/react';
// 下面2種引入方式也可以
// import { PersistGate } from 'redux-persist/lib/integration/react';
// import { PersistGate } from 'redux-persist/integration/react';
// import
import {store, persistStore} from './store';
import {RootComponent, LoadingView} from './components';
const App = () => {
return (
<Provider store={store}>
// loading 和 persistor是2個(gè)必需屬性
// loading={null} || loading={<LoadingView />} LoadingView為React組件
// 最好將loading={null}慧起,寫(xiě)成loading={<LoadingView />} 報(bào)錯(cuò),原因暫不明
<PersistGate loading={null} persistor={persistor}>
<RootComponent />
</PersistGate>
</Provider>
)
}
export default App;
自定義存儲(chǔ)內(nèi)容 (Customizing what's Persisted)
如果不想將部分state持久化册倒,可以將其放入黑名單(blacklist
)中.黑名單是設(shè)置PersistReducer
時(shí)傳入的配置對(duì)象
const persistConfig = {
key: 'root',
storage: storage,
blacklist: ['navigation']
};
const persistReducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(persistReducer);
export const persistor = persistStore(store);
黑名單接收字符串?dāng)?shù)組蚓挤。每個(gè)字符串必須匹配部分狀態(tài)(傳入persistReducer中reducer管理的狀態(tài))。上面的示例驻子,如果 rootReducer
通過(guò)createReducers
創(chuàng)建灿意,我們期望 navigation
像下面一樣存在在reducer中:
combineReducers({
auth: AuthReducer,
navigation: NavReducer, // navigation即reducer中的狀態(tài)
notes: NotesReducer
})
白名單(whitelist
)的設(shè)置和黑名單一樣,除了它表示的是你想要持久化的state\
const persistConfig = {
key: 'root',
storage: storage, // 或者是 AsyncStorage for React Native
whitelist: ['auth', 'notes']
}
假如你想要將一個(gè)嵌套的屬性加入黑名單怎么辦崇呵?例如缤剧,假設(shè)你的state對(duì)象有一個(gè)auth key,你想將auth.currentUser
持久化,而不持久化auth.isLoggingIn
為了完成這個(gè)任務(wù)域慷,使用PersistReducer
包裹AuthReducer
,然后將isLoggingIn
加入黑名單荒辕。這將允許共同定位持久化規(guī)則和它依附的reducer
// AuthReducer.js
import storage from 'reduc-persist/lib/storage';
import {persistReducer} from 'redux-persist';
const INITIAL_STATE = {
currentUser: null,
isLoggingIn: false
};
const AuthReducer = (state = INITIAL_STATE, action) => {
// reducer 實(shí)現(xiàn)汗销。。抵窒。
}
const persistConfig = {
key: 'auth',
storage: storage,
blacklist: ['isLoggingIn']
};
export default AuthReducer;
如果你更喜歡將所有的持久化規(guī)則都放在一起弛针,而不是單獨(dú)放在各自的reducer中,可以考慮將其放在combineReducers
函數(shù)中:
// src/reducers/index.js
import {combineReducers} from 'redux';
import storage from 'redux-persist/lib/storage';
import {persistReducer} from 'redux-persist';
import {authReducer, navReducer, notesReducer} from './reducers';
const rootRersistConfig = {
key: 'root',
storage: storage,
blacklist: ['navigation']
}
const authPersistConfig = {
key: 'auth',
storage: storage,
blacklist: ['isLoggingIn']
}
const rootReducer = combineReducers({
auth: persistReducer(authPersistConfig, authReducer),
navigation: navReducer,
notes: notesReducer
});
export default persistReducer(rootPersistConfig, rootReducer);
合并過(guò)程
當(dāng)應(yīng)用啟動(dòng)時(shí)估脆, redux設(shè)置一個(gè)初始狀態(tài),這之后座云,redux persist從storage中取回你持久化的state疙赠。然后取回的持久化state將覆蓋初始的state
合并過(guò)程是自動(dòng)工作的,但是你也可以手動(dòng)的處理這個(gè)過(guò)程朦拖。比如圃阳,以前redux-persist版本中通常通過(guò)捕獲reducers中REHYDRATE
動(dòng)作來(lái)管理rehydration過(guò)程,然后將action的payload存儲(chǔ)在redux的狀態(tài)中
import {REHYDRATE} from 'redux-persist';
const INITIAL_STATE = {
currentUser: null,
isLoggingIn: false
}
const AuthReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case REHYDRATE:
return {
...state,
currentUser: action.payload.currentUser
};
// ...其它的情況
}
}
REHYDRATE
動(dòng)作在persisted state從storage中獲取之后立即通過(guò)redux-persist發(fā)送出去璧帝。如果你從REHYDRATE
返回一個(gè)新的state 對(duì)象捍岳,這將是你最終的狀態(tài)。就如上面所說(shuō)的睬隶,現(xiàn)在再也不需要這樣做了锣夹,除非你需要自定義狀態(tài)rehydrated的方式
注意下面一些陷阱
這個(gè)陷阱來(lái)自合并過(guò)程,這和合并過(guò)程在state對(duì)變更的深入程度有關(guān)苏潜, 上面提到合并過(guò)程將覆蓋你的初始狀態(tài)银萍,無(wú)論你持久化了什么。下面是默認(rèn)的工作方式:
// 假設(shè)初始狀態(tài)如下恤左,并且將整個(gè)狀態(tài)都持久化
// initial state
{
auth: {
currentUser: null,
isLoggingIn: false
},
notes: []
}
// 一旦應(yīng)用啟動(dòng)
// 持久化狀態(tài)
{
auth: {
currentUser: {firstName: 'Mark', lastName: 'Newtorn'},
isLoggingIn: false
},
notes: [noteA, noteB, noteC]
}
默認(rèn)情況贴唇,合并過(guò)程簡(jiǎn)單的替換每個(gè)頂層的state,和下面方式類似:
const finialState = {...initialState};
finialState['auth'] = persisedState['auth'];
finialState['notes'] = persisedState['notes'];
這通常沒(méi)什么問(wèn)題,但是假如你想發(fā)布一個(gè)新版本的app,并且將初始狀態(tài)設(shè)置如下:
const INITIAL_STATE = {
currentUser: null,
isLoggingIn: false,
error: ''
}
很明顯你想在最終state中包含新的 error
key.但是持久化狀態(tài)對(duì)象中不存在這個(gè)error key, 它將在rehydration過(guò)程中完全的替換你的初始狀態(tài)飞袋,因此error key不會(huì)添加進(jìn)去戳气。
解決辦法是告訴 PersistReducer
合并 two-level
深度。在最上面巧鸭,你可能在root PersistReducerzhong 注意到了神秘的 stateReconciler
設(shè)置
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
const persistConfig = {
key: 'root',
storage: storage,
stateReconciler: autoMergeLevel2 // 神秘的stateReconciler
};
autoMergeLevel2
就是告訴 PersistReducer
合并 two-level
深度瓶您。對(duì)auth 狀態(tài),這意味著合并過(guò)程首先復(fù)制一份初始的auth state,然后只覆蓋auth對(duì)象中持久化的keys.由于 error
還沒(méi)有持久化纲仍,因此不會(huì)被丟棄览闰。
重要的是,我們需要知道PersistReducers默認(rèn)是 autoMergeLevel1
,這表示替換持久化頂層的狀態(tài)巷折。如果你沒(méi)有一個(gè)單獨(dú)的PersistReducer管理持頂層keys的持久化狀態(tài)压鉴,你可能需要使用 autoMergeLevel2
.
另外,redux-persist的作者意識(shí)到锻拘,選擇 autoMergeLevel1
還是 autoMergeLevel2
讓人感到困惑油吭,因此他創(chuàng)建了一個(gè)叫 persistCombineReduers
的函數(shù)來(lái)簡(jiǎn)化這個(gè)過(guò)程击蹲。這個(gè)函數(shù)的實(shí)現(xiàn)只有2行代碼,就是簡(jiǎn)單的給PersitReducer返回autoMergeLevel2
,我個(gè)人偏好是自己去管理level,而不是用這個(gè)函數(shù)婉宰。當(dāng)然這取決于你自己歌豺。
高級(jí)自定義
Transforms
轉(zhuǎn)換允許你自定義持久化和rehydrated 的state object。
當(dāng)state對(duì)象被持久化心包,它首先使用 JSON.stringify()
序列化state.如果你的部分state對(duì)象不能映射為JSON對(duì)象类咧,則序列化過(guò)程可能出錯(cuò)。例如蟹腾,js Set
數(shù)據(jù)類型在JSON中不存在痕惋,但你試著通過(guò)上面的方式序列化時(shí),Set類型的數(shù)據(jù)將轉(zhuǎn)換為一個(gè)空的對(duì)象娃殖。這可能不是你所期望的值戳。
下面是一個(gè)成功轉(zhuǎn)換Set數(shù)據(jù)類型的轉(zhuǎn)換(transform
),它簡(jiǎn)單的將其轉(zhuǎn)換為一個(gè)數(shù)組然后使用時(shí)再轉(zhuǎn)換回來(lái)。
import {createTransform} from 'redux-persist';
const SetTransform = createTransform(
// 在state被序列化和持久化的過(guò)程中進(jìn)行轉(zhuǎn)換
(inboundState, key) => {
// 將set轉(zhuǎn)換為array
return {...inboundSate, mySet: [...inboundState.mySet]};
},
// rehydrated的時(shí)候轉(zhuǎn)換
(outboundState, key) => {
return {...outboundState, mySet: new Set(outboundState.mySet)}
},
// 定義需要被轉(zhuǎn)換的reducer
{whitelist: ['someReducer']}
)
export default SetTransform;
createTransform
函數(shù)接收3個(gè)參數(shù):
- state持久化之前調(diào)用的函數(shù)
- 持久化數(shù)據(jù)轉(zhuǎn)變?yōu)閟tate前調(diào)用的函數(shù)(也稱之為
rehydrated
) - 一個(gè)配置對(duì)象
現(xiàn)在我們將transforms添加到 PersistReducer
的配置對(duì)象中:
import storage from 'redux-persist/lib/storage';
import { SetTransform } from './Transforms';
const persistConfig = {
key: 'root',
storage: storage,
transform: [SetTransform] // 添加轉(zhuǎn)換函數(shù)
}
// ...
文章來(lái)源:
相關(guān)文章:
- How to use Redux Persist when migrating your states
- Redux-persist: The Good Parts