How to persist and rehydrate a Redux store
Install redux-persist
npm i redux-persist
Setup and configure the persistor
// configureStore.js
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web
import rootReducer from './reducers'
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
export default () => {
let store = createStore(persistedReducer)
let persistor = persistStore(store)
return { store, persistor }
}
Wrap your root component with PersistGate
This will delay the rendering of your app’s UI until your persisted state has been retrieved and used to initialize your Redux store:
import { PersistGate } from 'redux-persist/integration/react'
// ... normal setup, create store and persistor, import components etc.
const App = () => {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<RootComponent />
</PersistGate>
</Provider>
);
};
Configure how incoming state is merged with initial state
redux-persist
gives you three options out of the box to control how the persisted / rehydrated state is merged with the initial state defined in your reducers.
autoMergeLevel1
(default)
The persisted state will be auto merged with the reducer’s initial state at one level deep. The reducer’s initial state will take precedence.
- Persisted state:
{ foo: persistedFoo, bar: persistedBar }
- Reducer initial state:
{ foo: reducerInitialFoo }
- Reconciled state:
{ foo: reducerInitialFoo, bar: persistedBar }
hardSet
The persisted state will hard set the initial state. Any initial state defined in the reducers will be ignored.
- Persisted state:
{ foo: persistedFoo }
- Reducer initial state:
{ foo: reducerInitialFoo, bar: reducerInitialBar }
- Reconciled state:
{ foo: persistedFoo }
Note how reducer’s initial state is completely ignored.
autoMergeLevel2
Not too sure how this works. Consult docs.
Whitelist or blacklist state slices
You can specify the set of state slices you don’t want to persist (blacklist):
const persistConfig = {
key: 'root',
storage: storage,
blacklist: ['navigation'] // navigation will not be persisted
}
Or specify the set of slices you want to persist (whitelist)
const persistConfig = {
key: 'root',
storage: storage,
whitelist: ['navigation'] // only navigation will be persisted
}
How to use with Redux Toolkit
Redux Toolkit uses the serializability middleware that warns (via console.warn
) if you’re using any non-serializable values in your state or in dispatched actions. You’ll want to suppress these warnings by doing something like this:
import { configureStore } from '@reduxjs/toolkit'
import {
persistStore,
persistReducer,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import { PersistGate } from 'redux-persist/integration/react'
import App from './App'
import rootReducer from './reducers'
const persistConfig = {
key: 'root',
version: 1,
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}),
})
let persistor = persistStore(store)
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>,
document.getElementById('root')
)
See the docs and this GitHub issue for more guidance.
Other notes
- You can use transforms to customize the state object that gets persisted or rehydrated. This is useful when your state contains data that cannot be serialized via
JSON.stringify
(e.g., aSet
object). - https://github.com/gabceb/redux-persist-transform-expire
Sources
Thanks for your comment 🙏. Once it's approved, it will appear here.
Leave a comment