redux-persist V5

在創(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ù):

  1. state持久化之前調(diào)用的函數(shù)
  2. 持久化數(shù)據(jù)轉(zhuǎn)變?yōu)閟tate前調(diào)用的函數(shù)(也稱之為 rehydrated)
  3. 一個(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)文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末炉爆,一起剝皮案震驚了整個(gè)濱河市堕虹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芬首,老刑警劉巖赴捞,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異郁稍,居然都是意外死亡螟炫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)艺晴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)昼钻,“玉大人,你說(shuō)我怎么就攤上這事封寞∪黄溃” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵狈究,是天一觀的道長(zhǎng)碗淌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)抖锥,這世上最難降的妖魔是什么亿眠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮磅废,結(jié)果婚禮上纳像,老公的妹妹穿的比我還像新娘。我一直安慰自己拯勉,他們只是感情好竟趾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布憔购。 她就那樣靜靜地躺著,像睡著了一般岔帽。 火紅的嫁衣襯著肌膚如雪玫鸟。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天犀勒,我揣著相機(jī)與錄音屎飘,去河邊找鬼。 笑死贾费,一個(gè)胖子當(dāng)著我的面吹牛钦购,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铸本,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肮雨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼遵堵!你這毒婦竟也來(lái)了箱玷?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤陌宿,失蹤者是張志新(化名)和其女友劉穎锡足,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體壳坪,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舶得,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了爽蝴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沐批。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蝎亚,靈堂內(nèi)的尸體忽然破棺而出九孩,到底是詐尸還是另有隱情,我是刑警寧澤发框,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布躺彬,位于F島的核電站,受9級(jí)特大地震影響梅惯,放射性物質(zhì)發(fā)生泄漏宪拥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一铣减、第九天 我趴在偏房一處隱蔽的房頂上張望她君。 院中可真熱鬧,春花似錦葫哗、人聲如沸犁河。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)桨螺。三九已至宾符,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灭翔,已是汗流浹背魏烫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肝箱,地道東北人哄褒。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像煌张,于是被迫代替她去往敵國(guó)和親呐赡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • title: 翻譯|A Dummy’s Guide to Redux and Thunk in Reactdate...
    smartphp閱讀 707評(píng)論 0 2
  • Redux是受到了Facebook Flux和Elm啟發(fā)的應(yīng)用構(gòu)架骏融。Redux使用了類似于Flux的單向數(shù)據(jù)流链嘀,但...
    心至靜行至遠(yuǎn)閱讀 3,149評(píng)論 1 21
  • 前言 本文 有配套視頻,可以酌情觀看档玻。 文中內(nèi)容因各人理解不同怀泊,可能會(huì)有所偏差,歡迎朋友們聯(lián)系我討論误趴。 文中所有內(nèi)...
    珍此良辰閱讀 11,904評(píng)論 23 111
  • http://gaearon.github.io/redux/index.html 霹琼,文檔在 http://rac...
    jacobbubu閱讀 79,956評(píng)論 35 198
  • 杭州的冬季真他媽操蛋,整天陰雨綿綿凉当,搞得我整個(gè)人也精神萎靡了枣申,自從來(lái)到701心情一直和這操蛋的天氣一樣多變,并且...
    孤島渡客閱讀 192評(píng)論 0 0