Persist Redux State with redux-persist

Persist Redux State with redux-persist

When we refresh page in a web-app, the state always resets back to the initial values which in not a good thing when you try to build some large web-app like e-commerce.

We can manually do the state persistent using the native JavaScript localStorage.setItem() method but honestly we will have to write everything from start and we have to maintain the structure of the state.

So here the redux-persist comes into play, with support for redux toolkit, that helps us to persist the state after page refresh.

##Redux-Persist

It is a package for persisting redux state when it is connected to the store. That's all what the package does. Pretty neat!

Without further talking, lets dive into how we actually hook it up with redux toolkit for state/store persisting. We will use a counter app for easier understanding.

###Step 1:

Install the package via npm or yarn (I will use npm here):

npm i redux-persist

###Step 2:

Add the required imports to the redux store:

//store.js
import storage from 'redux-persist/lib/storage';
import { combineReducers } from 'redux';
import {
	persistReducer,
	FLUSH,
	REHYDRATE,
	PAUSE,
	PERSIST,
	PURGE,
	REGISTER,
} from 'redux-persist';

redux-persist provides different storage to persist data like local storage, session storage or async storge. We will use the local storage.

We need the combineReducers function to group up all the reducers into one so that we can pass it to the redux-persist.

redux-persist dispatches some functions and according to official redux-toolkit guide we need to add those to the ignore list to avoid unnecessary warnings or errors.

###Step 3:

We need to create the persist object config that will be stored to the storage:

//store.js
const persistConfig = {
	key: 'counter',
	storage,
};

The key specifies the ID of the persist object and the storage determines the type of storage being used.

###Step 4:

Combine the reducers:

//store.js
const reducers = combineReducers({ counter: counterSlice });

###Step 5:

Create a persistent reducer:

///store.js
const persistedReducer = persistReducer(persistConfig, reducers);

###Step 6:

Assign the persist reducer to the reducers and extra dispatch functions to the ignore list in the middleware, at the end your store will look like this:

//store.js
export default configureStore({
	reducer: persistedReducer,
	middleware: (getDefaultMiddleware) =>
		getDefaultMiddleware({
			serializableCheck: {
				ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
			},
		}),
});

###Step 7:

Just like react-redux gives us Provider component that will wrap the whole app, similarly we get PersistGate from redux-persist. We need it to wrap around the whole app:

//index.js
import { persistStore } from 'redux-persist';
import { PersistGate } from 'redux-persist/integration/react';
//...
let persistor = persistStore(store);

ReactDOM.render(
	<React.StrictMode>
		<Provider store={store}>
			<PersistGate persistor={persistor}>
				<App />
			</PersistGate>
		</Provider>
	</React.StrictMode>,
	document.getElementById('root')
);

The persistStore will configure the store object to become compatible when we pass it to the PersistGate component.

###Step 8:

With all the connection done, now your react app can persist when when page changes and or the page reloads.

If we check it with redux-logger, we can see that it first checks with PERSIST action and rehydrate the store with the REHYDRATE action.

redux-logger showing redux-persist

##Wrapping Up

redux-persist is a great library for obvious reason, but it's way old and outdated, last update was at 2019 with several PR and issues at GitHub. I hope they revive the project and fix the necessary issues. Meanwhile, if you know any alternatives please let me know in the comments.