Integrating MMKV with Redux Toolkit for Persistent Data

Vinicius Petrachin
4 min readJan 7, 2023

--

MMKV Storage Benchmark in React Native

MMKV (Memory Mapped File Storage) is a high-performance, low-overhead key-value storage engine designed specifically for mobile platforms. It offers several benefits over AsyncStorage, making it a superior choice for React Native projects, including faster performance, more flexibility in terms of data types, and automatic data migration. Despite requiring an additional dependency, MMKV’s benefits may outweigh this inconvenience, making it a reliable choice for data persistence in your React Native project.

In contrast, AsyncStorage is a convenient solution for storing small amounts of data, but it may not always be the best choice for larger projects due to its slower performance and lack of automatic data migration. While it may be a suitable choice for web applications, MMKV is the better option for mobile projects due to its performance and flexibility advantages.

Step 1: Install dependencies

To get started, you will need to have Redux Toolkit and MMKV installed in your project. You can do this by running the following command in your terminal:

npm install @reduxjs/toolkit mmkv

Step 2: Set up your store

Next, we need to set up our Redux store with the configureStore function from Redux Toolkit. Import the function and add the following code to your store.js file:

import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducers';

const store = configureStore({
reducer: rootReducer,
});

export default store;

Step 3: Integrate MMKV with the store

To integrate MMKV with our store, we need to create a custom middleware. Add the following code to a new file called mmkvMiddleware.js:

import MMKV from 'mmkv';

const mmkv = new MMKV();

const mmkvMiddleware = () => next => action => {
const result = next(action);
mmkv.setItem('redux-state', store.getState());
return result;
};

export default mmkvMiddleware;

This middleware will intercept every action dispatched to the store and save the current state to MMKV after the action has been processed.

Step 4: Use the middleware in the store

Now that we have our custom middleware, we need to apply it to the store. Import the mmkvMiddleware function and add it to the middleware array in the configureStore function like this:

import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducers';
import mmkvMiddleware from './mmkvMiddleware';

const store = configureStore({
reducer: rootReducer,
middleware: [mmkvMiddleware],
});

export default store;

Step 5: Load data from MMKV on app startup

Finally, we need to load the saved state from MMKV when the app starts up. Add the following code to your root component (e.g. App.js):

import MMKV from 'mmkv';
import store from './store';

const mmkv = new MMKV();

const savedState = mmkv.getItem('redux-state');
if (savedState) {
store.dispatch({ type: 'LOAD_STATE', payload: savedState });
}

This code creates a new instance of MMKV and retrieves the saved state. If the state exists, it dispatches an action with the type ‘LOAD_STATE’ and the payload of the saved state. You will need to handle this action in your reducers to properly load the state into the store.

Step 6: (Optional) Create a scalable and robust integration

If you want to take your integration a step further and make it more scalable and robust, you can create separate folders for your middlewares and loaders.

First, create a new folder called middlewares and move the mmkvMiddleware.js file into it. Then, create a new folder called loaders and add a new file called mmkvLoader.js with the following code:

import MMKV from 'mmkv';
import store from '../store';

const mmkv = new MMKV();

const loadStateFromMMKV = () => {
const savedState = mmkv.getItem('redux-state');
if (savedState) {
store.dispatch({ type: 'LOAD_STATE', payload: savedState });
}
};

export default loadStateFromMMKV;

This code is similar to the code we added in step 5, but it has been separated into its own function for better organization.

Next, update your store.js file to import the mmkvMiddleware and the loadStateFromMMKV function, and apply the middleware and run the loader in the configureStore function like this:

import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducers';
import mmkvMiddleware from './middlewares/mmkvMiddleware';
import loadStateFromMMKV from './loaders/mmkvLoader';

const store = configureStore({
reducer: rootReducer,
middleware: [mmkvMiddleware],
});

loadStateFromMMKV();

export default store;

Step 7 (Optional): Enable data encryption with MMKV

If you want to add an extra layer of security to your application’s data, you can enable data encryption with MMKV. To do this, you will need to pass an additional option to the MMKV constructor when you create a new instance of the class.

import MMKV from 'mmkv';

const mmkv = new MMKV({ encryptionKey: 'my-secret-key' });

This will enable data encryption using the key specified in the encryptionKey option. Make sure to use a strong, unique key to ensure the security of your data.

You will also need to update the mmkvMiddleware function to pass the encryptionKey option when setting the item in MMKV:

import MMKV from 'mmkv';

const mmkv = new MMKV({ encryptionKey: 'my-secret-key' });

const mmkvMiddleware = () => next => action => {
const result = next(action);
mmkv.setItem('redux-state', store.getState(), { encryptionKey: 'my-secret-key' });
return result;
};

And update the loadStateFromMMKV function to pass the encryptionKey option when retrieving the item from MMKV:

import MMKV from 'mmkv';
import store from '../store';

const mmkv = new MMKV({ encryptionKey: 'my-secret-key' });

const loadStateFromMMKV = () => {
const savedState = mmkv.getItem('redux-state', { encryptionKey: 'my-secret-key' });
if (savedState) {
store.dispatch({ type: 'LOAD_STATE', payload: savedState });
}
};

By following these steps, you should now have a fully functional integration of MMKV with Redux Toolkit that allows you to persist your application’s data. Whether you choose the basic integration or the more scalable and robust approach, you can rest assured that your data will be safe and your application will maintain its state even when the page is refreshed or the tab is closed.

--

--

Vinicius Petrachin

Experienced React Native developer with a passion for mobile app development. Focusing on reaching seniority in React Native!