Async Storage in React Native

Jul 15, 2023 ⋅ Modified: Jul 15, 2023 ⋅ 5 min read

Bilal Arslan

Hi there! I'm a full-stack developer who loves crafting web and mobile apps. When I'm not coding, I enjoy sharing my knowledge as a technical writer. Let's build something awesome together!

Introduction

When developing a React Native application, there are times when you need to store persistent and small data on the device. This means that even if you restart the application, the data still exists! Examples of such data include session/JWT values, user data, or app configurations. To achieve this, Async Storage offers a solution. It is an asynchronous, unencrypted, persistent, key-value storage system for React Native.

Creating Project

npx create-expo-app react-native-asynstorage
  • Next, navigate to the project directory:
cd react-native-asynstorage

Installing Dependencies

  • Async Storage in React Native has been deprecated, so the "AsyncStorage" package no longer exists within the react-native package. Instead, you need to install third-party solutions. One of the most popular libraries for using AsyncStorage in React Native is "@react-native-async-storage/async-storage". However, there are other options available as well.
npm install @react-native-async-storage/async-storage

Starting the Emulator

npx expo start
  • Great! Before we start coding, let's take a closer look at some example use cases of Async Storage 🚀

Usage

  • Async Storage can only store string data. To store object data, you need to serialize it first. For data that can be serialized to JSON, you can use JSON.stringify() when saving the data and JSON.parse() when loading the data.
import AsyncStorage from '@react-native-async-storage/async-storage';
Storing String Value
const storeData = async (value) => {
  try {
    await AsyncStorage.setItem('my-key', value);
  } catch (e) {
    //error
  }
};
Reading String Value
const getData = async () => {
  try {
    const value = await AsyncStorage.getItem('my-key');
    if (value !== null) {
      //your data
    }
  } catch (e) {
    //error
  }
};
Storing Object Value
const storeData = async (value) => {
  try {
    const jsonValue = JSON.stringify(value);
    await AsyncStorage.setItem('my-key', jsonValue);
  } catch (e) {
    //error
  }
};
Reading Object Value
const getData = async () => {
  try {
    const jsonValue = await AsyncStorage.getItem('my-key');
    return jsonValue != null ? JSON.parse(jsonValue) : null;
  } catch (e) {
    //error
  }
};
Clear Data
removeValue = async () => {
  try {
    await AsyncStorage.removeItem('my-key')
  } catch(e) {
    // remove error
  }
}
Clearing All
const clearAllData = async () => {
  try {
    await AsyncStorage.clear();
  } catch (e) {
    //error
  }
};

Implementation

  • For this tutorial, we will create a basic app that reads, writes, and removes a username 🚀
import React, { useState, useEffect } from 'react';
import { Text, View, TextInput, TouchableOpacity, Dimensions, Keyboard } from 'react-native';
import { FontAwesome } from '@expo/vector-icons';
import AsyncStorage from '@react-native-async-storage/async-storage';
 
export default function App() {
 
  const [userName, setUsername] = useState('');
 
  return (
    <View style={{ flex: 1, paddingVertical: 30, paddingHorizontal: 20, backgroundColor: '#fdfdfd' }}>
 
      <FontAwesome name='user-circle' size={200} color='#22a6b3' style={{ alignSelf: 'center', marginTop: 80 }} />
 
      <Text style={{ alignSelf: 'center', margin: 8, color: '#2c3e50', fontSize: 16 }}>What is your username?</Text>
      <Text style={{ alignSelf: 'center', margin: 8, color: '#2c3e50', fontSize: 16 }}>{userName}</Text>
 
 
      <View style={{ marginTop: 16, padding: 8, borderRadius: 10, borderWidth: 1.5, borderColor: '#22a6b3' }}>
        <TextInput placeholder='Username' value={userName} onChangeText={setUsername} />
      </View>
 
      <TouchableOpacity style={{ backgroundColor: '#22a6b3', marginTop: 32, padding: 16, borderRadius: 10, justifyContent: 'center', alignItems: 'center' }}>
        <Text style={{ color: '#fdfdfd', fontWeight: '600' }}>Save</Text>
      </TouchableOpacity>
 
      <TouchableOpacity style={{ backgroundColor: '#22a6b3', marginTop: 16, padding: 16, borderRadius: 10, justifyContent: 'center', alignItems: 'center' }}>
        <Text style={{ color: '#fdfdfd', fontWeight: '600' }}>Remove</Text>
      </TouchableOpacity>
    </View>
  );
}
  • The UI looks like this;
App Screen
  • Let's implement the Async Storage business logic
  const save = async () => {
    try {
      await AsyncStorage.setItem('my-user-name', username);
      Keyboard.dismiss();
    } catch (e) {
      console.log('Error when saving..');
    }
  }
 
  const remove = async () => {
    try {
      await AsyncStorage.removeItem('my-user-name');
      setUsername('');
    } catch (e) {
      console.log('Error when removing..');
    }
  }
 
  const load = async () => {
    try {
      const username = await AsyncStorage.getItem('my-user-name');
      if (username !== null) {
        setUsername(username);
      }
    } catch (e) {
      console.log('Error when loading..');
    }
  }
 
  useEffect(() => {
    load();
  }, []);
  • Update the buttons' behavior to call methods
  <TouchableOpacity style={{ backgroundColor: '#22a6b3', marginTop: 32, padding: 16, borderRadius: 10, justifyContent: 'center', alignItems: 'center' }}
    onPress={() => save()}>
    <Text style={{ color: '#fdfdfd', fontWeight: '600' }}>Save</Text>
  </TouchableOpacity>
 
  <TouchableOpacity style={{ backgroundColor: '#22a6b3', marginTop: 16, padding: 16, borderRadius: 10, justifyContent: 'center', alignItems: 'center' }}
  onPress={() => remove()}>
    <Text style={{ color: '#fdfdfd', fontWeight: '600' }}>Remove</Text>
  </TouchableOpacity>
  • When the application is first opened, the load function is triggered by useEffect, so if there is data in Async Storage, it will automatically set the data!
import React, { useState, useEffect } from 'react';
import { Text, View, TextInput, TouchableOpacity, Dimensions, Keyboard } from 'react-native';
import { FontAwesome } from '@expo/vector-icons';
import AsyncStorage from '@react-native-async-storage/async-storage';
 
export default function App() {
 
  const [username, setUsername] = useState('');
 
  const save = async () => {
    try {
      await AsyncStorage.setItem('my-user-name', username);
      Keyboard.dismiss();
    } catch (e) {
      console.log('Error when saving..');
    }
  }
 
  const remove = async () => {
    try {
      await AsyncStorage.removeItem('my-user-name');
      setUsername('');
    } catch (e) {
      console.log('Error when removing..');
    }
  }
 
  const load = async () => {
    try {
      const username = await AsyncStorage.getItem('my-user-name');
      if (username !== null) {
        setUsername(username);
      }
    } catch (e) {
      console.log('Error when loading..');
    }
  }
 
  useEffect(() => {
    load();
  }, []);
 
  return (
    <View style={{ flex: 1, paddingVertical: 30, paddingHorizontal: 20, backgroundColor: '#fdfdfd' }}>
 
      <FontAwesome name='user-circle' size={200} color='#22a6b3' style={{ alignSelf: 'center', marginTop: 80 }} />
 
      <Text style={{ alignSelf: 'center', marginTop: 32, color: '#2c3e50', fontSize: 16 }}>What is your username?</Text>
      <Text style={{ alignSelf: 'center', margin: 8, color: '#2c3e50', fontSize: 16, fontWeight: '600' }}>{username}</Text>
 
 
      <View style={{ marginTop: 16, padding: 8, borderRadius: 10, borderWidth: 1.5, borderColor: '#22a6b3' }}>
        <TextInput placeholder='Username' value={username} onChangeText={setUsername} />
      </View>
 
      <TouchableOpacity style={{ backgroundColor: '#22a6b3', marginTop: 32, padding: 16, borderRadius: 10, justifyContent: 'center', alignItems: 'center' }}
        onPress={() => save()}>
        <Text style={{ color: '#fdfdfd', fontWeight: '600' }}>Save</Text>
      </TouchableOpacity>
      <TouchableOpacity style={{ backgroundColor: '#22a6b3', marginTop: 16, padding: 16, borderRadius: 10, justifyContent: 'center', alignItems: 'center' }}
        onPress={() => remove()}>
        <Text style={{ color: '#fdfdfd', fontWeight: '600' }}>Remove</Text>
      </TouchableOpacity>
    </View>
  );
}

Important

  • It is not recommended to save crucial data in Async Storage due to security concerns. Always use encryption for sensitive information.

Project Dependencies

"dependencies": {
    "@react-native-async-storage/async-storage": "^1.19.0",
    "expo": "~48.0.18",
    "expo-status-bar": "~1.4.4",
    "react": "18.2.0",
    "react-native": "0.71.8"
  }

Conclusion

  • In conclusion, Async Storage proves to be a valuable tool for React Native developers seeking a simple and efficient way to store small, persistent data on mobile devices. However, it's essential to exercise caution and avoid storing critical or sensitive data without encryption. By following these guidelines and leveraging the power of Async Storage, you can enhance your React Native applications with persistent and seamless data handling capabilities. Happy coding! 🚀