๐ฑ Introduction
When you create an App, you'll most likely need to store and manipulate data for it, and React Native Async Storage makes it really easy to include such functionality.
It works almost the same as the browser's local storage. The data is stored as a string (a JSON.stringified object), and the API offers a (short) list of methods to perform any CRUD operations.
Since the data is unencrypted, you probably don't want to save sensitive data like passwords or anything auth-related. A typical use case would rather be the user's highscores in a game app, or their progress in a habit/exercise tracker app.
The default maximum size of the storage on Android is 6 MB, but it can be increased by adding one line to the android/gradle.properties
:
AsyncStorage_db_size_in_MB=10
๐ฑ Basic usage
At the most basic level, you only need three methods to create/read/delete data. Operations on the storage are asynchronous, you can use async/await
syntax in a try/catch
block.
Installation:
npm i @react-native-async-storage/async-storage
Importing:
import AsyncStorage from '@react-native-async-storage/async-storage';
Create/save
const data = {key1: 'value-1', key2: 'value-2'};
const dataString = JSON.stringify(data);
AsyncStorage.setItem('my-storage-key', dataString);
Read
AsyncStorage.getItem('my-storage-key');
Delete
To delete the whole storage (not advised, because some of the packages you're using in your app might depend on the storage as well):
AsyncStorage.clear()
To delete the data for a specific key:
AsyncStorage.removeItem('my-storage-key');
๐ฑ Usage in a real React Native Application
I'll take my current project as an example: I recently discovered yoga (on a sidenote, it's awesome, your programmer's back will be most grateful), and now I'm building an app to keep track of my progress. Below is a screenshot of the current UI (the stats button leads to a different view to visually represent the progress, the seed and clear buttons are only used during development):
It's the kind of yoga where you try to hold a certain position for as long as possible, which I measure in number of breaths. I've also noticed strong differences between left and right side of the body, so I keep separate records for those. The entry for one day would thus be a key/value pair with the date being the key, and the value being an array of objects with the following structure:
"2021/07/19": [ {exercise: "Eagle", "left": 12, "right": 20}, { ... }, { ... } ]
In the first iteration, I had stored everything under one key "progress", but decided to keep separate entries for each day instead. As the data grows, I'd otherwise have to rewrite the whole data as one value each time, instead of just adding another key/value pair. It also makes it easier to implement the possibility to update/delete an entry.
Creating the data with multiSet
For development, I've implemented two functions to seed the storage with a few entries, and to delete the whole storage. multiSet
, as the name suggests, allows to set multiple entries at once, and accepts an array of arrays [key, value]
.
const seedData = [
{
date: '2021/07/12',
exercises: [
{ title: 'Eagle', left: '6', right: '12' },
{ title: 'Warrior III', left: '5', right: '7' },
{ title: 'Side Plank', left: '10', right: '7' },
],
},
{
date: '2021/07/13',
exercises: [
{ title: 'Eagle', left: '7', right: '12' },
{ title: 'Warrior III', left: '7', right: '7' },
{ title: 'Side Plank', left: '13', right: '9' },
],
},
{
date: '2021/07/17',
exercises: [
{ title: 'Eagle', left: '10', right: '20' },
{ title: 'Warrior III', left: '9', right: '9' },
{ title: 'Side Plank', left: '16', right: '9' },
],
},
];
const seedStorage = () => {
// prepare the data for multiSet
const dataArr = seedData.map((item) => [
item.date,
JSON.stringify(item.exercises),
]);
const storeData = async (arr) => {
try {
await AsyncStorage.multiSet(arr);
getData(); // see below
} catch (err) {
console.warn(`ERROR in seedStorage: ${err}`);
}
};
storeData(dataArr);
};
(That data is real, btw. You can either conclude that I'm in terrible shape, or that I'm making awesome progress.)
Deleting all data with multiRemove
The multiRemove
method accepts an array of keys. Those can be retrieved with .getAllKeys
(make sure that the only stored entries are from your code, and not something that one of your packages might depend on).
const removeEntries = async () => {
const keys = await AsyncStorage.getAllKeys();
try {
await AsyncStorage.multiRemove(keys);
getData(); // see below
} catch (err) {
console.warn(`ERROR in removeEntries: ${err}`);
}
};
Reading the data with multiGet
To retrieve the data when the app loads, I first need to get all the stored keys with .getAllKeys
again. The multiGet
method accepts an array of keys and returns an array of arrays [key, value]
:
const [ data, setData ] = useState(null);
const getData = async () => {
try {
const keys = await AsyncStorage.getAllKeys();
if (keys.length > 0) {
const storageJSON = await AsyncStorage.multiGet(keys);
const storageData = storageJSON.map((item) => [
item[0],
JSON.parse(item[1]),
]);
setData(storageData);
} else {
setData(null);
}
} catch (err) {
console.warn(`ERROR in getData: ${err}`);
}
};
useEffect(() => {
getData();
}, []);
This function also runs after every storage update, for example after deletion of all entries, hence the else
statement to make sure that the UI reflects the current state of the storage.
Adding an entry with setItem
In the below code, I've left out the pure React parts that deal with getting the data from my input fields and creating the new date
string and entry
object.
When submitting a new entry, I first check if there's already an entry for that day in storage, the rest is completely straightforward:
const storeData = async (date, entry) => {
const keys = await AsyncStorage.getAllKeys();
if (keys.includes(date)) {
console.warn('This day already has an entry');
return;
}
try {
await AsyncStorage.setItem(date, entry);
getData();
} catch (err) {
console.warn(`ERROR in storeData: ${err}`);
}
};
Editing and deleting an entry
Since the data is stored in JSON.stringified format, you can't update the object directly in storage, but overwrite it with setItem
. Delete an entry with removeItem
(examples for both methods above).
๐ฑ Resources
React Native Async Storage API
๐ฑ Thanks for reading!
If you find any errors or have additions, questions or just want to say hi, please leave a comment below, or get in touch via my website jsdisco.dev or Twitter.
Keep calm & code ๐