Persisting data in React Native

Introduction

When building React Native applications, there are several ways to persist data, with each having its own strong advantage. In this piece, we shall discuss the most popular ways to persist data in our React Native application.

React Native lets you build mobile apps using only JavaScript. It uses the same design as React, which allows you to build the mobile UIs as components.

Prerequisites

A basic understanding of JavaScript and familiarity with React/React Native will be required to understand how to use libraries and configure your React Native application to persist data.

Methods of persisting data in React Native

React Native provides methods to persist data. Some are provided out of the box while others are libraries you have to install and use. Below are popular methods of persisting data in React Native

  • AsyncStorage
  • React Native SQLite 2
  • Realm
  • React Native Local MongoDB

AsyncStorage

According to the official React Native docs, this is the most recommended way to persist data in your React Native applications as it is already built into React Native.

AsyncStorage functions like the HTML5 local storage class and it uses key-value pairs to persist data.

Data saved using the Asyncstorage class are usually not permanent on the device and are usually not encrypted, therefor when using this method, you must provide your backup and synchronization classes.

NOTE: Do not use AsyncStorage if you'll be dealing with a large amount of data in your application.

Security

AsyncStorage encrypts none of the data saved. All objects are stored as strings and must be serialized before they can be saved and likewise be de-serialized before retrieval.

Usage

Import the AsyncStorage library:

    import { AsyncStorage } from "react-native";

To persist data:

1// create a function that saves your data asyncronously
2    _storeData = async () => {
3        try {
4            await AsyncStorage.setItem('name', 'John');
5        } catch (error) {
6            // Error saving data
7        }
8    }

To fetch persisted data:

1// fetch the data back asyncronously
2    _retrieveData = async () => {
3        try {
4            const value = await AsyncStorage.getItem('name');
5            if (value !== null) {
6                // Our data is fetched successfully
7                console.log(value);
8            }
9        } catch (error) {
10            // Error retrieving data
11        }
12    }

React Native SQLite 2

This is a plugin which provides a WebSQL API to persist data. It provides SQL-like syntax, for querying your in-app persisted database.

It has support for both Android, iOS, and windows. It's also considered being a drop-in replacement with react-native-sqlite-storage.

Security

SQLite doesn't support encryption out of the box, but with an extension named SQLite Encryption Extension (SEE) which is provided on their official website. SEE allows SQLite to read/write data in an encrypted format using algorithms like AES-128, AES-256, RC4, and so on.

Aside from SEE, there are other extensions that allow an encryption to be possible such as SQLCipher.

Usage

To get started, install react-native-sqlite-2:

    $ npm install react-native-sqlite-2 --save

Link the library dependency:

    $ react-native link react-native-sqlite-2

Import the library and persist data:

    import SQLite from 'react-native-sqlite-2';

Open the database file:

    const db = SQLite.openDatabase('test.db', '1.0', '', 1);

Create a transaction function to execute your SQL statements:

1db.transaction(function (txn) {
2    
3        // Drop the table if it exists
4        txn.executeSql('DROP TABLE IF EXISTS Users', []);
5    
6        // Create the table and define the properties of the columns
7        txn.executeSql('CREATE TABLE IF NOT EXISTS Users(user_id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(30))', []);
8    
9        // Insert a record
10        txn.executeSql('INSERT INTO Users (name) VALUES (:name)', ['nora']);
11    
12        // Insert another record
13        txn.executeSql('INSERT INTO Users (name) VALUES (:name)', ['takuya']);
14    
15        // Select all inserted records, loop over them while printing them on the console.
16        txn.executeSql('SELECT * FROM `users`', [], function (tx, res) {
17            for (let i = 0; i < res.rows.length; ++i) {
18                console.log('item:', res.rows.item(i));
19            }
20        });
21    
22    });

Realm

Realm was designed and built from scratch to support mobile devices and even wearables. It's designed as an object-oriented database and this makes it up to 10x faster than SQLite. It's simple to use since it exposes data as objects and it supports relationships among data, mapping classes, tables, foreign keys and so on.

Realm can be combined with server-side databases to allow seamless synchronization of data offline to the cloud/server database. Realm is a fantastic choice if you'll be dealing with large data in your application.

Security

Realm is dynamic with the way it encrypts data. It uses different encryption mechanism for different mobile platform. For Android, Realm uses the AES-256 level of encryption and decryption of all the data stored locally. For iOS applications, Realm's encryption is based on the iOS CommonCrypto library. The library protects the application data and passwords stored in the keychain. While for Windows applications, Realm uses Crypto library for encryption.

Usage

To get started with realm in React Native:

    $ npm install realm --save

Link the library dependency:

    $ react-native link realm

Import the library:

    const Realm = require('realm');

Initiate the realm state to null:

1constructor(props) {
2        super(props);
3        this.state = { realm: null };
4    }

Persist data:

1// Write it into the realm state as soon as the component mounts
2    componentWillMount() {
3    
4        // Open a Realm Schema 
5        Realm.open({
6            schema: [{name: 'Dog', properties: {name: 'string'}}]
7        }).then(realm => {
8    
9            // Write a record into the realm schema
10            realm.write(() => {
11                realm.create('Dog', {name: 'Rex'});
12            });
13    
14            // Update the realm state
15            this.setState({ realm });
16        });
17    }

Fetch and render:

1render() {
2        const info = this.state.realm ? 'Number of dogs in this Realm: ' + this.state.realm.objects('Dog').length: 'Loading...';
3        return (
4            <View style={styles.container}></View>
5            <Text style={styles.welcome}>
6                {info}
7            </Text>
8            </View>
9        );
10    }

Putting it together as a component:

1// import the library 
2    const Realm = require('realm');
3    
4    class RealmDb extends Component {
5    
6        //initiate the realm state to null
7        constructor(props) {
8            super(props);
9            this.state = { realm: null };
10        }
11    
12        //persist data and write it into the realm state as soon as the component mounts
13        componentWillMount() {
14    
15        // Open a Realm Schema 
16        Realm.open({
17            schema: [{name: 'Dog', properties: {name: 'string'}}]
18        }).then(realm => {
19    
20            // Write a record into the realm schema
21            realm.write(() => {
22                realm.create('Dog', {name: 'Rex'});
23            });
24    
25            // Update the realm state
26            this.setState({ realm });
27        });
28    
29    }
30    
31        // render a Text component with the value
32        render() {
33            const info = this.state.realm ? 'Number of dogs in this Realm: ' + this.state.realm.objects('Dog').length: 'Loading...';
34            return (
35                <View style={styles.container}></View>
36                <Text style={styles.welcome}>
37                    {info}
38                </Text>
39                </View>
40            );
41        }
42    }

React Native Local MongoDB

MongoDB is an open-source server-side database built for scalability and complex applications and Big data. It uses key-value stores and a relational database to store data as objects in JSON documents.

MongoDB can read and write JavaScript objects and allows smooth communication between the server and app. It's a right choice if you'll be dealing with large data in your application.

However, MongoDB can be slow for connected models that require joins.

Security

MongoDB supports encryption directly out of the box, unlike SQLite. According to the official docs on encryption, It uses the MongoDB’s Encrypted Storage Engine, which supports a variety of encryption algorithms from the OpenSSL library. AES-256 in CBC mode is the default, while other options include GCM mode and FIPS mode for FIPS-140-2 compliance. It also uses the SCRAM-SHA-1 algorithm mechanism for authentication and a role-based access control.

Usage

To get started with using MongoDB in your React Native application:

    $ npm install react-native-local-mongodb --save

Create a datastore:

1var Datastore = require('react-native-local-mongodb'), 
2    db = new Datastore({ filename: 'asyncStorageKey', autoload: true });

Insert into your database:

1db.insert([{ a: 5 }, { a: 42 }], function (err, newDocs) {
2        // Two documents were inserted in the database
3    });

Find a document:

1db.find({ system: 'solar' }, function (err, docs) {
2        // docs is an array containing documents Mars, Earth, Jupiter
3        // If no document is found, docs is equal to []
4    });

Update a document:

1db.update({ planet: 'Jupiter' }, { planet: 'Pluton'}, {}, function (err, numReplaced) {
2        // numReplaced = 1
3        // The doc #3 has been replaced by { _id: 'id3', planet: 'Pluton' }
4        // Note that the _id is kept unchanged, and the document has been replaced
5    });

Remove a document:

1db.remove({ _id: 'id2' }, {}, function (err, numRemoved) {
2        // numRemoved = 1   
3    });

Indexing:

1db.ensureIndex({ fieldName: 'somefield' }, function (err) {
2        // If there was an error, err is not null
3    });

Conclusion

React Native provides several ways to persist data and your choice should depend on the type and amount of data you'll be handling. We have seen four methods of persisting data in React Native, each with its own strength. AsyncStorage is great for storing small data which needs to be serialized. You shouldn't use AsyncStorage for handling relational data in any form. If you are interested in storing relational data, look more into SQLite. If SQLite is too slow for what you want to achieve, then Realm is the better solution. If you are storing JavaScript objects to your database, then you should consider MongoDB.