How to Implement Local Storage in Your Expo App?

Share this Content

If you’re developing a mobile app that uses local mobile data, then local storage is important, as it enhances user experience by ensuring data persistence and offline functionality. In React Native Expo, there are three primary local storage options: AsyncStorage, SecureStore, and SQLite. Each of these solutions serves different purposes, offering unique benefits and addressing specific needs.

In this guide, you will learn how to implement these local storage solutions in your React Native Expo projects. Whether you need a simple key-value store, secure storage for sensitive information, or a robust database for handling large datasets, this guide will provide you with the knowledge and tools to make the right choice.

Are you ready to optimize your app’s data storage capabilities? Let’s explore the best options for your needs.

Overview of Local Storage Options

In React Native Expo, three primary local storage solutions are commonly used: AsyncStorage, SecureStore, and SQLite. Each option serves different purposes, offering unique benefits and drawbacks. Understanding these options will help you decide which one best fits your application’s requirements.

1. AsyncStorage

AsyncStorage is a simple, unencrypted, asynchronous, persistent key-value storage system that is global to the app. It is commonly used for small amounts of data that need to be persisted across app launches.

ProsConsUse Cases
Easy to useUnencryptedStoring user preferences and settings
Asynchronous operationsPerformance issues with large datasetsCaching small pieces of data
Persistent storageLimited storage capacitySaving non-sensitive information that needs to persist between sessions

2. SecureStore

SecureStore offers a more secure alternative for storing sensitive information. It uses the device’s secure storage mechanisms, such as the iOS Keychain or Android’s Keystore system, to store data securely.

ProsConsUse Cases
High securityLimited storage capacityStoring authentication tokens and API keys
Platform integrationSynchronous operationsSaving user credentials securely
Ideal for sensitive dataSlightly more complex to implementKeeping sensitive configuration settings

3. SQLite

SQLite is a powerful, embedded SQL database engine. It allows you to create complex relational databases and perform SQL queries directly within your application. Expo provides support for SQLite through the expo-sqlite package.

ProsConsUse Cases
Robust database managementRequires knowledge of SQL and database managementManaging large datasets and complex data relationships
Efficient storageMore involved setupOffline-first applications requiring robust data storage
Persistent and reliablePerformance overheadApps needing advanced querying and data manipulation capabilities

Each of these storage solutions offers distinct advantages and trade-offs. Choosing the right one depends on your app’s specific needs, such as the type of data you are storing, the required security level, and performance considerations. Understanding these options will help you make an informed decision and implement the best solution for your application.

Implementing AsyncStorage

AsyncStorage provides a simple, unencrypted, asynchronous key-value storage solution for React Native applications. It is often used for storing small amounts of data that need to persist across app launches, such as user preferences or temporary data.

To begin using AsyncStorage in your React Native Expo project, you need to install the @react-native-async-storage/async-storage package. Use the following command to install it:

npx expo install @react-native-async-storage/async-storage

After installing the package, you can import it into your project and start using its methods.

AsyncStorage Methods

AsyncStorage provides several methods for interacting with the storage system. Here, we will cover the most commonly used methods: setItem, getItem, removeItem, mergeItem, getAllKeys, and clear.

setItem

The setItem method allows you to store a value associated with a specific key. This method is asynchronous and returns a promise.

import AsyncStorage from '@react-native-async-storage/async-storage';

const storeData = async (key, value) => {
  try {
    await AsyncStorage.setItem(key, value);
    console.log('Data stored successfully');
  } catch (error) {
    console.error('Error storing data', error);
  }
};

// Usage
storeData('user_name', 'John Doe');
JavaScript

getItem

The getItem method retrieves the value associated with a specific key. It returns a promise that resolves to the stored value.

const getData = async (key) => {
  try {
    const value = await AsyncStorage.getItem(key);
    if (value !== null) {
      console.log('Retrieved data:', value);
    }
  } catch (error) {
    console.error('Error retrieving data', error);
  }
};

// Usage
getData('user_name');
JavaScript

removeItem

The removeItem method deletes the value associated with a specific key from the storage.

const removeData = async (key) => {
  try {
    await AsyncStorage.removeItem(key);
    console.log('Data removed successfully');
  } catch (error) {
    console.error('Error removing data', error);
  }
};

// Usage
removeData('user_name');
JavaScript

mergeItem

The mergeItem method merges an existing value with the new value for a specific key. This is particularly useful for objects.

const mergeData = async (key, value) => {
  try {
    await AsyncStorage.mergeItem(key, value);
    console.log('Data merged successfully');
  } catch (error) {
    console.error('Error merging data', error);
  }
};

// Usage
mergeData('user_settings', JSON.stringify({ theme: 'dark' }));
JavaScript

getAllKeys

The getAllKeys method retrieves all keys stored in the AsyncStorage. It returns a promise that resolves to an array of keys.

const getAllKeys = async () => {
  try {
    const keys = await AsyncStorage.getAllKeys();
    console.log('All keys:', keys);
  } catch (error) {
    console.error('Error retrieving keys', error);
  }
};

// Usage
getAllKeys();
JavaScript

clear

The clear method removes all key-value pairs from the storage.

const clearStorage = async () => {
  try {
    await AsyncStorage.clear();
    console.log('Storage cleared successfully');
  } catch (error) {
    console.error('Error clearing storage', error);
  }
};

// Usage
clearStorage();
JavaScript

By following these methods and best practices, you can efficiently manage local data storage in your React Native Expo application using AsyncStorage.

Implementing SecureStore

SecureStore offers a secure way to store sensitive information in React Native applications. It leverages the device’s secure storage mechanisms, such as the iOS Keychain or Android’s Keystore system, to ensure data security.

To use SecureStore in your React Native Expo project, you need to install the expo-secure-store package. Use the following command to install it: npx expo install expo-secure-store

After installing the package, you can import it into your project and start using its methods.

SecureStore Methods

SecureStore provides several methods for secure data storage: setItemAsync, getItemAsync, deleteItemAsync, and isAvailableAsync.

setItemAsync

The setItemAsync method allows you to store a value associated with a specific key securely. This method is asynchronous and returns a promise.

Subscribe to Tech Break
import * as SecureStore from 'expo-secure-store';

const storeSecureData = async (key, value) => {
  try {
    await SecureStore.setItemAsync(key, value);
    console.log('Data stored securely');
  } catch (error) {
    console.error('Error storing secure data', error);
  }
};

// Usage
storeSecureData('secure_token', 'your_secure_token');
JavaScript

getItemAsync

The getItemAsync method retrieves the value associated with a specific key securely. It returns a promise that resolves to the stored value.

const getSecureData = async (key) => {
  try {
    const value = await SecureStore.getItemAsync(key);
    if (value !== null) {
      console.log('Retrieved secure data:', value);
    }
  } catch (error) {
    console.error('Error retrieving secure data', error);
  }
};

// Usage
getSecureData('secure_token');
JavaScript

deleteItemAsync

The deleteItemAsync method deletes the value associated with a specific key from the secure storage.

const deleteSecureData = async (key) => {
  try {
    await SecureStore.deleteItemAsync(key);
    console.log('Secure data deleted successfully');
  } catch (error) {
    console.error('Error deleting secure data', error);
  }
};

// Usage
deleteSecureData('secure_token');
JavaScript

isAvailableAsync

The isAvailableAsync method checks if the secure storage is available on the device. It returns a promise that resolves to a boolean value.

const checkSecureStoreAvailability = async () => {
  try {
    const isAvailable = await SecureStore.isAvailableAsync();
    console.log('SecureStore availability:', isAvailable);
  } catch (error) {
    console.error('Error checking SecureStore availability', error);
  }
};

// Usage
checkSecureStoreAvailability();
JavaScript

By using these methods, you can securely manage sensitive data in your React Native Expo application using SecureStore.

Using SQLite

SQLite is a powerful, embedded SQL database engine that allows you to create complex relational databases and perform SQL queries directly within your React Native Expo application. It is ideal for handling large datasets and complex data relationships.

To use SQLite in your React Native Expo project, you need to install the expo-sqlite package. Use the following command to install it: npx expo install expo-sqlite

After installing the package, you can import it into your project and start using its methods.

SQLite Methods

SQLite provides several methods for managing databases: openDatabase, transaction, and SQL execution methods like executeSql.

openDatabase

The openDatabase method opens a database and returns a database object. If the database does not exist, it is created.

import * as SQLite from 'expo-sqlite';

const db = SQLite.openDatabase('myDatabase.db');

// Usage
const openDatabase = () => {
  return SQLite.openDatabase('myDatabase.db');
};

const db = openDatabase();
JavaScript

transaction

The transaction method creates a transaction that allows multiple SQL statements to be executed as a single unit of work.

db.transaction(tx => {
  // SQL statements go here
});
JavaScript

executeSql

The executeSql method executes a SQL statement against the database. It can be used within a transaction.

const createTable = () => {
  db.transaction(tx => {
    tx.executeSql(
      'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY NOT NULL, name TEXT, age INTEGER);'
    );
  });
};

// Usage
createTable();
JavaScript

Basic CRUD Operations

  1. Create (Insert Data)

To insert data into a table, use the executeSql method within a transaction.

const insertData = (name, age) => {
  db.transaction(tx => {
    tx.executeSql(
      'INSERT INTO users (name, age) values (?, ?)',
      [name, age],
      (txObj, resultSet) => console.log('Data inserted', resultSet),
      (txObj, error) => console.error('Error inserting data', error)
    );
  });
};

// Usage
insertData('John Doe', 30);
JavaScript
  1. Read (Query Data)

To retrieve data from a table, use the executeSql method within a transaction.

const queryData = () => {
  db.transaction(tx => {
    tx.executeSql(
      'SELECT * FROM users',
      [],
      (txObj, { rows: { _array } }) => console.log('Data retrieved', _array),
      (txObj, error) => console.error('Error querying data', error)
    );
  });
};

// Usage
queryData();
JavaScript
  1. Update (Modify Data)

To update existing data in a table, use the executeSql method within a transaction.

const updateData = (id, name, age) => {
  db.transaction(tx => {
    tx.executeSql(
      'UPDATE users SET name = ?, age = ? WHERE id = ?',
      [name, age, id],
      (txObj, resultSet) => console.log('Data updated', resultSet),
      (txObj, error) => console.error('Error updating data', error)
    );
  });
};

// Usage
updateData(1, 'Jane Doe', 25);
JavaScript
  1. Delete (Remove Data)

To delete data from a table, use the executeSql method within a transaction.

const deleteData = (id) => {
  db.transaction(tx => {
    tx.executeSql(
      'DELETE FROM users WHERE id = ?',
      [id],
      (txObj, resultSet) => console.log('Data deleted', resultSet),
      (txObj, error) => console.error('Error deleting data', error)
    );
  });
};

// Usage
deleteData(1);
JavaScript

By following these methods, you can effectively manage complex data relationships and handle large datasets in your React Native Expo application using SQLite.

Troubleshooting Common Issues

When implementing local storage in React Native Expo, you may encounter several common issues. Here, we’ll discuss these issues and provide practical solutions to help you navigate these challenges effectively.

AsyncStorage Issues

  1. Data Not Persisting Across Sessions
    One common issue with AsyncStorage is that data does not persist across app restarts. This often occurs due to improper handling of asynchronous operations. Ensure you are correctly using await with the setItem method.
   await AsyncStorage.setItem('key', 'value');
JavaScript
  1. Performance Degradation with Large Data Sets
    AsyncStorage is designed for small amounts of data. When you try to store large datasets, you might notice significant performance issues. It’s best to use AsyncStorage for simple key-value pairs and small data. For larger datasets, consider using SQLite, which is better suited for handling extensive data efficiently.
  2. Inconsistent Data Retrieval
    Another issue you might encounter is inconsistent data retrieval, where getItem sometimes returns null. This usually happens due to key mismatches or asynchronous handling errors. Double-check that the key used in getItem is exactly the same as the one used in setItem and that you are properly awaiting the call.
   const value = await AsyncStorage.getItem('key');
JavaScript

SecureStore Issues

  1. Data Not Being Stored Securely
    SecureStore is designed to store data securely, but improper use can lead to unencrypted data storage. Always use the SecureStore.setItemAsync method, which encrypts the data by default.
   await SecureStore.setItemAsync('key', 'value');
JavaScript
  1. SecureStore Not Available on Device
    SecureStore may not be available on all devices or platforms, leading to failures. Before using SecureStore, check its availability on the device.
   const isAvailable = await SecureStore.isAvailableAsync();
   if (!isAvailable) {
     console.log('SecureStore is not available on this device.');
   }
JavaScript
  1. Data Retrieval Issues
    Sometimes, getItemAsync may return null even if data was stored correctly. This is often due to key mismatches. Ensure the key used in getItemAsync matches the key used in setItemAsync.
   const value = await SecureStore.getItemAsync('key');
JavaScript

SQLite Issues

  1. Database Not Opening
    A common issue with SQLite is the failure to open the database. Ensure the correct database path and filename are used, and that the app has the necessary permissions.
   const db = SQLite.openDatabase('myDatabase.db');
JavaScript
  1. SQL Syntax Errors
    SQL queries might fail due to syntax errors. Always double-check your SQL syntax and use placeholders for variables to avoid SQL injection and syntax errors.
   db.transaction(tx => {
     tx.executeSql(
       'INSERT INTO users (name, age) values (?, ?)',
       ['John Doe', 30],
       (txObj, resultSet) => console.log('Data inserted'),
       (txObj, error) => console.error('SQL Error', error)
     );
   });
JavaScript
  1. Transactions Not Executing
    SQL transactions might not execute, leading to data not being written to the database. Ensure all SQL statements within a transaction are valid and use the correct callback functions to handle success and error scenarios.
   db.transaction(tx => {
     tx.executeSql(
       'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY NOT NULL, name TEXT, age INTEGER);'
     );
   });
JavaScript
  1. Performance Issues with Large Datasets
    Queries may become slow with large datasets. To optimize performance, use indexing and efficient query structures.
   db.transaction(tx => {
     tx.executeSql(
       'CREATE INDEX IF NOT EXISTS idx_users_name ON users (name);'
     );
   });
JavaScript

Navigating these common issues will help you use local storage solutions more effectively in your React Native Expo projects. Remember, each storage option has its strengths and ideal use cases, so choose the one that best fits your app’s requirements. If you face other challenges, consulting the official documentation and community forums can provide additional support.

Conclusion

Local data storage is essential for enhancing user experience and ensuring data persistence in mobile applications. In this guide, we explored three primary local storage solutions in React Native Expo: AsyncStorage, SecureStore, and SQLite.

AsyncStorage is ideal for storing small amounts of data, such as user preferences and settings. It’s easy to use but should be avoided for sensitive data due to its lack of encryption.

SecureStore offers a secure way to store sensitive information, such as authentication tokens and user credentials. It ensures high security but is limited in storage capacity and slightly more complex to implement.

SQLite provides robust database management capabilities for handling large datasets and complex data relationships. It’s suitable for offline-first applications and those requiring advanced querying capabilities, though it requires knowledge of SQL and careful management.

When choosing a local storage solution, consider the type of data, required security, and performance implications. Properly implementing these storage methods can significantly enhance your application’s reliability and user experience.

Have you identified which storage solution best fits your app’s needs? Experiment with these options and optimize your data storage strategy accordingly. If you encounter challenges, refer to the official documentation and community resources for additional guidance.

Share this Content
Snehasish Konger
Snehasish Konger

Snehasish Konger is the founder of Scientyfic World. Besides that, he is doing blogging for the past 4 years and has written 400+ blogs on several platforms. He is also a front-end developer and a sketch artist.

Articles: 201

Newsletter Updates

Join our email-newsletter to get more insights