ReactNative: Use react-native-mmkv to improve application performance

When working with React Native, you’ve most likely already used React AsyncStorageas a storage solution. For example, you can use AsyncStorageto store key-value pairs, such as your application’s current theme, or even store state and tokens for various reasons.

In addition AsyncStorage, we can also use some third-party storage solutions. In this article, we’ll look at react-native-mmkvthe library and explore why you might want to use it instead AsyncStorage, and how to use it in our applications.

Why use react-native-mmkv?

The react-native-mmkv library developed by WeChat allows us to efficiently store and read key-value pairs from the MMKV storage framework. Its name is memory-map key-valueshort for React Native Storage.

Similar to AsyncStorage, react-native-mmkvit also has cross-platform compatibility, meaning it works on both iOS and Android platforms. Let’s look at some reasons why you might consider using MMKV instead of AsyncStorage.

encryption

AsyncStorageIt is an unencrypted storage system. AsyncStorageStorage solutions like are not recommended for storing passwords, tokens, and other private information.

MMKVMore secure than AsyncStorage, offering data encryption and other more advanced security features. If you want to store sensitive data that requires a high level of security, MMKV is a better choice.

Fully synchronized storage

AsyncStorageis an asynchronous storage system that utilizes async/await 与promises. In contrast, react-native-mmkvall calls to are fully synchronous and can therefore be made without using any promises.

Let’s take a look at the code below for better understanding:

// AsyncStorage

// storing data 
const  storeUser = async ( value ) => {
   try {
     await  AsynStorage . setItem ( "user" , JSON . stringify (value));
  } catch (error) {
     console . log (error);
  }
};
storeUser ( "Chimezie" )

// getting data 
const  getUser = async () => {
   try {
     const userData = await  AsynStorage . getItem ( "user" )
     const user = JSON . parse (userData)
  } catch (error) {
    console . log (error);
  }
};

If you look at the code above, you can see that we are using both when storing and retrieving stored data async/await.

Using react-native-mmkv, instead of issuing a Promise request, we can call it as follows:

// react-native-mmkv

storage.set ( ' username' , 'Chimezie' );

const username = storage.getString( 'username' )

In the example above react-native-mmkv, we are setting our username into our storage. When we want to retrieve data, we use the getString() method because we are getting string data. react-native-mmkvDoes not AsyncStoragereturn one like promise.

Serialization

If you look at our example above, you’ll notice that we are AsyncStorageconverting the user’s value that we want to store in into a string. This is because AsyncStorageis dealing with string values, so we have to serialize all non-string data types before saving.

const  storeUser = async () => {
   try {
     const userData = {
         name : "Chimezie" ,
         location : "Nigeria"
    }
    const serializedUser = JSON . stringify (userData)
     await  AsynStorage . setItem ( "user" , serializedUser);
  } catch (error) {
     console . log (error);
  }
};

Likewise, to AsyncStorageretrieve data using

const  getUser = async () => {
   try {
     const userData = await  AsynStorage . getItem ( "user" )
     const userObject = JSON . parse (userData)
  } catch (error) {
    console . log (error);
  }
};

For MMKV, this is not the case. In this regard, MMKV is more efficient as it supports different basic types or data types such as booleans, numbers, and strings. Simply put, you don’t need to manually serialize all values ​​before storing them.

storage . set ( 'username' , 'Innocent' ) // string
 storage . set ( 'age' , 25 ) // number
 storage . set ( 'is-mmkv-fast-asf' , true ) // boolean

Faster performance

Since react-native-mmkv non-string data is not serialized and parsed, it is AsyncStoragefaster than . The image below, from the MMKV team, shows benchmark results for the time it takes to read data a thousand times from different storage solutions. MMKV proves to be faster than all other schemes:

Additionally, because MMKV is fully synchronous, it eliminates promisethe stress of waiting for completion to get data. This makes reading and writing data much easier and faster – without having to deal with any promiseor error logic.

Limitations of react-native-mmkv

Although react-native-mmkvthe library has many advantages, it also has some limitations. In this section, we will discuss some things to pay attention to when using MMKV.

debug

MMKVUtilizing the JavaScript Interface (JSI), it provides synchronous local access to improve efficiency and performance. However, this poses a challenge for remote debugging because tools like Chrome DevTools use the traditional React Native bridge rather than the JSI bridge.

As a workaround for this limitation, you can use the Flipper debugging tool . Flipper is designed for debugging when your application is JSI enabled or your application uses a JSI library like MMKV. Alternatively, you can log your errors to the console for debugging.

memory size

The MMKV library is very efficient for storing small amounts of data such as user preferences, theme status, or application settings. There are no specific size measurements or limits, but MMKV is recommended for storing small data.

However, since it provides in-memory storage, storing large amounts of data will consume memory and hinder application performance. Therefore, it is not recommended to use MMKV to store large amounts of data.

document

Unlike AsyncStoragereact-native-mmkvdocumentation for the library is very limited. The only documentation available is the README.md file in the library’s GitHub repository, which explains how to use the library.

Using react-native-mmkv

We have now seen some of the reasons why you might consider using MMKV instead of AsyncStorage, as well as some of its limitations. In this section, we will look at how to use react-native-mmkvpackages to store and retrieve key-value data in our application .

MMKV does not work in Expo, so you can use it in a bare React Native project, or by pre-building and popping your Expo app. To install the package, run any of the following commands:

// npm 
npm install react- native -mmkv

//yarn 
yarn add react- native -mmkv

Next, we will create an instance and then initialize it. We can then call this instance anywhere in the application.

Create a Storage.js file called and copy the code below:

// Storage.js

import { MMKV } from  'react-native-mmkv'

export  const storage = new  MMKV ({
   id : `user-storage` ,
   path : ` ${USER_DIRECTORY} /storage` ,
   encryptionKey : 'encryptionkey' 
})

In the above code, we first import the installed package. Next, we create an storageinstance named which can accept three options – idpathand encryptionKey.

idIs a unique identifier used to distinguish MMKV instances. You can create different instances to store different kinds of data. idHelps distinguish or separate them:

const passwordStorage = new MMKV({
   id: `password-storage`,
})

const themeStorage = new MMKV({
   id: `theme-storage`,
})

pathIt is the root file where MMKV stores data on your device. It also allows you to customize the path or directory to your liking.

encryptionKeyis a unique key used to encrypt data before storage and decrypt it after retrieval.

After we create the instance, we can import the instance and use it in any component.

Storing data

As we saw before, MMKV supports different data types, which means we can store different data types without serializing them.

First, let’s import our installed packages:

//App.js

import { storage } from  './Storage'

Next, we will use storage to store our data:

storage . set ( 'username' , 'Innocent' ) // string
 storage . set ( 'age' , 25 ) // number
 storage . set ( 'is-mmkv-fast-asf' , true ) // boolean

That’s it! As you can see in the code comments above, react-native-mmkvstring, numeric, and boolean data types are supported. However, with objects and arrays, we must serialize the data before we can save it.

// objects
const user = {
   name : "Chimezie",
   location : "Nigeria",
  email: 'chimezieinnocent39@gmail.com' ,
}
storage . set ("userDetails", JSON .stringify( user ))

// arrays
const numberArray = [ 1 , 2 , 3 , 4 , 5 ];
const 
serializedArray = JSON .stringify(numberArray);
 storage .set ( 'numbers' , serializedArray);

Retrieve data

Retrieving data using MMKV is as simple and straightforward as storing it. You use getString()to get string data, to getNumber()get numeric data, and to getBoolean()get boolean data:

const username = storage.get String(' username ')  // 'Innocent' 
const age = storage.get Number(' age ')  // 25 
const isMmkvFastAsf = storage.get Boolean(' is - mmkv - fast - asf ')  // true

For objects and arrays, we will use getString()because we serialize it before saving it – in other words, store it as string data. We will then use JSON.parse()to deserialize or convert back to the original state or data type.

// objects 
const serializedUser = storage. getString ( 'userDetails' );
 const userObject = JSON . parse (serializedUser);

console . log (userObject); 
 /* output: const user = {
  name: "Chimezie",
  location: "Nigeria",
  email: 'chimezieinnocent39@gmail.com',
} */


// arrays 
const serializedArray = storage. getString ( 'numbers' );
 const numberArray = JSON . parse (serializedArray);

console . log (numberArray); // Output: [1, 2, 3, 4, 5]

Additionally, in the case where you want to view all keys in the store, MMKV getAllKeys()allows us to do this by providing a method called . This method returns an array containing all keys stored in the instance:

// Set  some key- value pairs
 storage . set ( 'name' , 'Innocent' );
 storage . set ( 'age' , 25 );

const allKeys = storage .getAllKeys();
console.log(allKeys); // Output: [ 'name' , 'age' ]

delete data

Deleting data using MMKV is similar AsyncStorage. We can delete specific keys or all keys in our instance:

//  delete a key
storage.delete ( 'username ' )

//  delete all keys
storage.clearAll()

Encrypt data

Data encryption is AsyncStorageanother advantage of MMKV compared to . MMKV provides the option to encrypt data before storing it, but AsyncStoragedoes not provide encryption functionality.

Before encrypting any data, we must first provide an encryption key in our instance. MMKV uses this key to encrypt and decrypt data:

// Storage.js

import { MMKV } from  'react-native-mmkv'

export  const storage = new  MMKV ({
   id : `user-storage` ,
   encryptionKey : 'EncrypTedKey123' 
})

We can then encrypt any data we want like this:

// App.js
 storage . set ( 'userPassword' , 'This is a secret user password' );

// Retrieving data from the encrypted  storage 
const password = storage .getString( 'userPassword' );

console.log( password ); // Output: 'This is a secret user password

Subscribe to updates

react-native-mmkvAllows us to subscribe to updates or changes to key-value data. To subscribe to updates, we can addOnValueChangedListener()register an event listener using the method. Whenever the specified key-value pair data changes, the listener will be notified.

Let’s see how we can do this:

//App.tsx

import  React , {useState, useEffect} from  'react' ;
 import { Colors } from  'react-native/Libraries/NewAppScreen' ;
 import {
   Text ,
   View ,
   Button ,
   StatusBar ,
   StyleSheet ,
   SafeAreaView ,
} from  'react-native' ;
 import {storage} from  './Storage' ;

function  App (): JSX . Element {
   const [isDarkMode, setIsDarkMode] = useState<boolean>( false );
   const backgroundStyle = {
     backgroundColor : isDarkMode ? Colors . darker : Colors . lighter ,
  };

  useEffect ( () => {
     const listener = storage. addOnValueChangedListener ( changedKey => {
       if (changedKey === 'isDarkMode' ) {
         const newValue = storage. getBoolean (changedKey);
         console . log ( 'theme:' , newValue) ;
      }
    });

    return  () => {
      listener.remove ( );
    };
  }, []);

  const  toggleTheme = () => {
     const newMode = !isDarkMode;
     setIsDarkMode (newMode);
    storage.set ( ' isDarkMode ' , newMode);
  };

  return (
     < SafeAreaView  style = {[backgroundStyle,  styles.sectionContainer ]}> 
      < StatusBar 
        barStyle = {isDarkMode ? ' light-content ' : ' dark-content '}
         backgroundColor = {backgroundStyle.backgroundColor} 
      /> 
      < View > 
        < Text 
          style = {[ 
            styles.sectionTitle ,
            {
              color:  isDarkMode ? Colors.white  :  Colors.black ,
            },
          ]}>
          {isDarkMode ? 'Dark Mode' : 'Light Theme'}
        </ Text > 
        < Text 
          style = {[ 
            styles.sectionDescription ,
            {
              color:  isDarkMode ? Colors.light  :  Colors.dark ,
            },
          ]}>
          React Native MMKV Tutorial
        </ Text > 
        < Button 
          onPress = {toggleTheme} 
          title = {isDarkMode ? ' Switch  to  light  mode ' : ' Switch  to  dark  mode '}
        /> 
      </ View > 
    </ SafeAreaView >
  );
}
const styles = StyleSheet . create ({
   sectionContainer : {
     flex : 1 ,
     justifyContent : 'center' ,
     alignItems : 'center' ,
  },
  sectionTitle : {
     fontSize : 24 ,
     fontWeight : '600' ,
     textAlign : 'center' ,
  },
  sectionDescription : {
     marginVertical : 8 ,
     fontSize : 18 ,
     fontWeight : '400' ,
     textAlign : 'center' ,
  },
  highlight : {
     fontWeight : '700' ,
  },
});
export  default  App ;

In the above code, we use addOnValueChangedListener()the method to register a callback function. This function listens for changes to a specific key in the MMKV store.

The callback function takes changedKeyas an argument, storage.getBoolean(changedKey)gets the new value associated with that key, and logs its new value to the console.

Whenever a key-value pair in MMKV is modified, a callback function will be called with the name of the key that was changed, allowing you to react to the change in your application.

Finally, we useEffectunsubscribe the listener in the return function. This is done to avoid or prevent memory leaks when our component is unloaded.

Summarize

In this article, we explore some of the reasons you might consider using MMKV instead of AsyncStorage and consider some of its limitations. You can check out the code examples we used in this GitLab repository.

It is true that MMKV is newer than AsyncStorage and does not have as broad a community as AsyncStorage. However, MMKV provides a faster, more efficient, and more secure storage system. This library has over 50 contributors and is extremely well maintained.