在创建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个参数:
- state持久化之前调用的函数
- 持久化数据转变为state前调用的函数(也称之为
rehydrated
) - 一个配置对象
现在我们将transforms添加到 PersistReducer
的配置对象中:
import storage from 'redux-persist/lib/storage';
import { SetTransform } from './Transforms';
const persistConfig = {
key: 'root',
storage: storage,
transform: [SetTransform] // 添加转换函数
}
// ...
文章来源:
相关文章:
- How to use Redux Persist when migrating your states
- Redux-persist: The Good Parts