redux-persist V5

在创建redux store 时, 将persistReducer包装应用的rootReducer,然后传递给createStore函数。一旦store创建完成,将其传递给persistStore函数,用来确保redux状态当发生变化时能够持久化存储

// 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函数 这个export
export const persistor = persistStore(store);  // 包装store 这个也export

如果使用React,则使用 PersistGate 包裹根组建。这将延迟渲染app UI直到持久化状态取回并保存到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个必需属性
            // loading={null} || loading={<LoadingView />} LoadingView为React组件
            // 最好将loading={null},写成loading={<LoadingView />} 报错,原因暂不明
            <PersistGate loading={null} persistor={persistor}>
                <RootComponent />
            </PersistGate>
        </Provider>
    )
}
export default App;

自定义存储内容 (Customizing what's Persisted)

如果不想将部分state持久化,可以将其放入黑名单(blacklist)中.黑名单是设置PersistReducer时传入的配置对象

const persistConfig = {
  key: 'root',
  storage: storage,
  blacklist: ['navigation']
};

const persistReducer = persistReducer(persistConfig, rootReducer);

export const store = createStore(persistReducer);
export const persistor = persistStore(store);

黑名单接收字符串数组。每个字符串必须匹配部分状态(传入persistReducer中reducer管理的状态)。上面的示例,如果 rootReducer通过createReducers创建,我们期望 navigation 像下面一样存在在reducer中:

combineReducers({
  auth: AuthReducer,
  navigation: NavReducer, // navigation即reducer中的状态
  notes: NotesReducer
})

白名单(whitelist)的设置和黑名单一样,除了它表示的是你想要持久化的state\

const persistConfig = {
  key: 'root',
  storage: storage,  // 或者是 AsyncStorage for React Native
  whitelist: ['auth', 'notes']
}

假如你想要将一个嵌套的属性加入黑名单怎么办?例如,假设你的state对象有一个auth key,你想将auth.currentUser持久化,而不持久化auth.isLoggingIn

为了完成这个任务,使用PersistReducer包裹AuthReducer,然后将isLoggingIn加入黑名单。这将允许共同定位持久化规则和它依附的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 实现。。。
}

const persistConfig = {
  key: 'auth',
  storage: storage,
  blacklist: ['isLoggingIn']
};

export default AuthReducer;

如果你更喜欢将所有的持久化规则都放在一起,而不是单独放在各自的reducer中,可以考虑将其放在combineReducers函数中:

// 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);

合并过程

当应用启动时, redux设置一个初始状态,这之后,redux persist从storage中取回你持久化的state。然后取回的持久化state将覆盖初始的state

合并过程是自动工作的,但是你也可以手动的处理这个过程。比如,以前redux-persist版本中通常通过捕获reducers中REHYDRATE动作来管理rehydration过程,然后将action的payload存储在redux的状态中

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动作在persisted state从storage中获取之后立即通过redux-persist发送出去。如果你从REHYDRATE返回一个新的state 对象,这将是你最终的状态。就如上面所说的,现在再也不需要这样做了,除非你需要自定义状态rehydrated的方式

注意下面一些陷阱

这个陷阱来自合并过程,这和合并过程在state对变更的深入程度有关, 上面提到合并过程将覆盖你的初始状态,无论你持久化了什么。下面是默认的工作方式:

// 假设初始状态如下,并且将整个状态都持久化
// initial state
{
  auth: {
    currentUser: null,
    isLoggingIn: false
  },
  notes: []
}

// 一旦应用启动
// 持久化状态
{
  auth: {
    currentUser: {firstName: 'Mark', lastName: 'Newtorn'},
    isLoggingIn: false
  },
  notes: [noteA, noteB, noteC]
}

默认情况,合并过程简单的替换每个顶层的state,和下面方式类似:

const finialState = {...initialState};
finialState['auth'] = persisedState['auth'];
finialState['notes'] = persisedState['notes'];

这通常没什么问题,但是假如你想发布一个新版本的app,并且将初始状态设置如下:

const INITIAL_STATE = {
  currentUser: null,
  isLoggingIn: false,
  error: ''
}   

很明显你想在最终state中包含新的 error key.但是持久化状态对象中不存在这个error key, 它将在rehydration过程中完全的替换你的初始状态,因此error key不会添加进去。

解决办法是告诉 PersistReducer合并 two-level 深度。在最上面,你可能在root PersistReducerzhong 注意到了神秘的 stateReconciler 设置

import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
const persistConfig = {
 key: 'root',
 storage: storage,
 stateReconciler: autoMergeLevel2  // 神秘的stateReconciler
};

autoMergeLevel2 就是告诉 PersistReducer合并 two-level 深度。对auth 状态,这意味着合并过程首先复制一份初始的auth state,然后只覆盖auth对象中持久化的keys.由于 error 还没有持久化,因此不会被丢弃。

重要的是,我们需要知道PersistReducers默认是 autoMergeLevel1,这表示替换持久化顶层的状态。如果你没有一个单独的PersistReducer管理持顶层keys的持久化状态,你可能需要使用 autoMergeLevel2.

另外,redux-persist的作者意识到,选择 autoMergeLevel1还是 autoMergeLevel2让人感到困惑,因此他创建了一个叫 persistCombineReduers的函数来简化这个过程。这个函数的实现只有2行代码,就是简单的给PersitReducer返回autoMergeLevel2,我个人偏好是自己去管理level,而不是用这个函数。当然这取决于你自己。

高级自定义

Transforms

转换允许你自定义持久化和rehydrated 的state object。

当state对象被持久化,它首先使用 JSON.stringify() 序列化state.如果你的部分state对象不能映射为JSON对象,则序列化过程可能出错。例如,js Set 数据类型在JSON中不存在,但你试着通过上面的方式序列化时,Set类型的数据将转换为一个空的对象。这可能不是你所期望的。

下面是一个成功转换Set数据类型的转换(transform),它简单的将其转换为一个数组然后使用时再转换回来。

import {createTransform} from 'redux-persist';

const SetTransform = createTransform(
  // 在state被序列化和持久化的过程中进行转换
  (inboundState, key) => {
    // 将set转换为array
    return {...inboundSate, mySet: [...inboundState.mySet]};
  },
  // rehydrated的时候转换
  (outboundState, key) => {
      return {...outboundState, mySet: new Set(outboundState.mySet)}
  },
  // 定义需要被转换的reducer
  {whitelist: ['someReducer']}
)
export default SetTransform;

createTransform函数接收3个参数:

  1. state持久化之前调用的函数
  2. 持久化数据转变为state前调用的函数(也称之为 rehydrated)
  3. 一个配置对象

现在我们将transforms添加到 PersistReducer的配置对象中:

import storage from 'redux-persist/lib/storage';
import { SetTransform } from './Transforms';

const persistConfig = {
  key: 'root',
  storage: storage,
  transform: [SetTransform]  // 添加转换函数
}

// ...

文章来源:

相关文章:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容