Replace Async Storage with MMKV in React Native
Tags: reactnative, javascript, performance, mobile
Published: 2023-07-08 13:58:15

## [React-Native MMKV](https://github.com/mrousavy/react-native-mmkv#readme)

- The fastest key/value storage for React Native.

- MMKV is an efficient, small mobile key-value storage framework developed by WeChat.

- react-native-mmkv is a library that allows you to easily use MMKV inside your React Native applications. It provides fast and direct bindings to the native C++ library which are accessible through a simple JS API.

---

### Features

1.  Get and set strings, booleans and numbers
2. Fully synchronous calls, no async/await, no Promises, no Bridge.
3. Encryption support (secure storage)
4. Multiple instances support (separate user-data with global data)
5. Customize storage location
6. High performance because everything is written in C++
7. ~30x faster than AsyncStorage
8. Uses JSI instead of the "old" Bridge
9. iOS, Android and Web support
10. Easy to use React Hooks API
11. No Async/Await

##Limitations:
[Blog](https://blog.logrocket.com/using-react-native-mmkv-improve-app-performance/#limitations-react-native-mmkv)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r6t642o3zsz24ga02yhk.png)
[Click here](https://github.com/mrousavy/StorageBenchmark) to compare popular storage solutions for React Native.

---

### Installation

```
yarn add react-native-mmkv && cd ios && pod install
```
or
```
npm install react-native-mmkv && cd ios && pod install
```

---

**[Click here](https://github.com/mrousavy/react-native-mmkv#set) to check how to store/retrieve values in app**

**[Click here](https://github.com/mrousavy/react-native-mmkv/blob/master/docs/HOOKS.md) to see how to use it as an hook like useState**

**[Click here](https://github.com/mrousavy/react-native-mmkv/blob/master/docs/LISTENERS.md) to add listener when ever some key changes and you want to perform some action on that**

**[CLick here](https://github.com/mrousavy/react-native-mmkv/blob/master/docs/WRAPPER_REDUX.md) to see how to integrate MMKV with redux-persist**

**[Click here](https://www.npmjs.com/package/reactotron-react-native-mmkv) to use reactotron-react-native-mmkv to automatically log writes to your MMKV storage using Reactotron.**

---

# Usage with redux persist and reactotron

You should re-use this instance throughout your entire app instead of creating a new instance each time, so export the storage object.

> 1. Make a **MMKVStorage.js** helper file and paste the below code:

`MMKVStorage.js`
```
import { MMKV } from 'react-native-mmkv';
import { ACCESS_TOKEN, BIOMETRICS_LOGGED_IN } from './Constants';

export const storage = new MMKV();

/*
//Encrypted storage
export const storage = new MMKV({
  id: `user-${userId}-storage`, //required: if when either path/encryptionKey exist
  path: `${USER_DIRECTORY}/storage`, //optional: changing storage path
  encryptionKey: 'hunter2' //optional: storing all values encrypted
})
*/

//TO BE USED IN REDUX PERSIST
export const reduxPersistStorage = {
  setItem: (key, value) => {
    storage.set(key, value);
    return Promise.resolve(true);
  },
  getItem: (key) => {
    const value = storage.getString(key);
    return Promise.resolve(value);
  },
  removeItem: (key) => {
    storage.delete(key);
    return Promise.resolve();
  },
};

//HELPER FUNCTIONS TO BE USED THROUGHOUT APP
export const StorageMMKV = {
  setUserPreferences: (key, value) => {
    try {
      storage.set(`${key}`, `${value}`);
    } catch (error) {
      console.error('Error setting user preferences:', error);
    }
  },

  getUserPreferences: (key) => {
    try {
      return storage.getString(key);
    } catch (error) {
      console.error('Error getting user preferences:', error);
      return null; // Or handle the error according to your application's logic
    }
  },

  removeItem: (key) => {
    try {
      storage.delete(key);
    } catch (error) {
      console.error('Error removing item:', error);
    }
  },

  clearAll: () => {
    try {
      storage.clearAll();
    } catch (error) {
      console.error('Error clearing storage:', error);
    }
  },

  setLoggedInByBiometric: (val) => {
    try {
      if (val) {
        storage.set(`${BIOMETRICS_LOGGED_IN}`, `${val}`);
      } else {
        storage.delete(BIOMETRICS_LOGGED_IN);
      }
    } catch (error) {
      console.error('Error setting logged in by biometric:', error);
    }
  },

  addRemoveDeviceToken: (val) => {
    try {
      if (val) {
        storage.set(`${ACCESS_TOKEN}`, `${val}`);
      } else {
        storage.delete(ACCESS_TOKEN);
      }
    } catch (error) {
      console.error('Error adding/removing device token:', error);
    }
  },
};

```

> 2 . Create a **ReactotronConfig.js** file:

`ReactotronConfig.js`
```
import Reactotron from 'reactotron-react-native';
import { reactotronRedux } from 'reactotron-redux';
import mmkvPlugin from 'reactotron-react-native-mmkv';
import { MMKV } from 'react-native-mmkv';

//hack to avoid multiple reconnection of device on refresh
const REACTOTRON_ASYNC_CLIENT_ID = '@REACTOTRON/clientId';
const storage = new MMKV({ id: REACTOTRON_ASYNC_CLIENT_ID });
const getClientId = async () => storage.getString(REACTOTRON_ASYNC_CLIENT_ID);
const setClientId = async (clientId) => {
  storage.set(REACTOTRON_ASYNC_CLIENT_ID, clientId);
};

Reactotron.configure({
  getClientId,
  setClientId,
})
  .useReactNative()
  .use(reactotronRedux()) //  <- here i am!
  .use(
    mmkvPlugin({
      storage, // mmkv instance
      ignore: ['secret', 'persist:root'],
      // "persist:root" key will avoid showing redux persist update every time redux persist
    })
  )
  .connect();

const yeOldeConsoleLog = console.log;
// make a new one
console.log = (...args) => {
  // always call the old one, because React Native does magic swizzling too
  yeOldeConsoleLog(...args);
  // send this off to Reactotron.
  Reactotron.display({
    name: 'CONSOLE',
    value: args,
    preview: args.length > 0 && typeof args[0] === 'string' ? args[0] : null,
  });
};

export default Reactotron;
```

> 3 . Update your `store.js` like this for reactotron and mmkv configuration:

`store.js`
```
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import { persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER } from 'redux-persist';
import userSlice from './slices/userSlice';
import nonPersistedSlice from './slices/nonPersistedSlice';
import { reduxPersistStorage } from '../helpers/MMKVStorage';

const persistConfig = {
  key: 'root',
  version: 1,
  storage: reduxPersistStorage, // MMKVStorage,
  blacklist: ['nonPersistedSlice'], // these reduce will not persist data
  whitelist: ['userSlice'], // these reduce will persist data
};
const reducer = combineReducers({
  userSlice,
  nonPersistedSlice,
});
const persistedReducer = persistReducer(persistConfig, reducer);

const getEnhancers = (getDefaultEnhancers) => {
  if (process.env.NODE_ENV === 'development') {
    const reactotron = require('../helpers/ReactotronConfig').default;
    return getDefaultEnhancers().concat(reactotron.createEnhancer());
  }
  return getDefaultEnhancers();
};

// https://redux-toolkit.js.org/usage/usage-guide#use-with-redux-persist
const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], //
      },
    }),
  enhancers: getEnhancers, // REACTOTRON CONFIGURATION
});

export default store;

```

> 4 . Finally update your `App.jsx`:

`App.js`
```
// /**
//  * Sample React Native App
//  * https://github.com/facebook/react-native
//  *
//  * @format
//  * @flow strict-local
//  */

import React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { persistStore } from 'redux-persist';
// import store from './redux/store';
import rtkStore from './reduxToolkit/store';
import HomeScreen from './screens/home';

let persistor = persistStore(rtkStore);

const App = () => {

  const getASBiometricValue = async () => {

    const bv = await StorageMMKV.getUserPreferences(BIOMETRICS_LOGGED_IN);
    setIsSelectedBiometric(bv);
  };

  useEffect(() => {
getASBiometricValue(
  }, []);

  return (
    <Provider store={rtkStore}>
      <PersistGate loading={null} persistor={persistor}>
        <HomeScreen />
      </PersistGate>
    </Provider>
  );
};
export default App;
```

[Source Code Github Link - >](https://github.com/Ajmal0197/RN_Dev_Codes/tree/main/srcReduxToolkit)