Skip to main content

Data persistance

To ensure our shopping list does not disappear every time we kill the app, we'll need to store it in the device's storage.

For this, we'll be using AsyncStorage - an unencrypted key-value store for React Native. It's very similar to localStorage on the web, only as the name suggests, the calls to is are asynchronous.

Install Async Storage

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

util function

There's two parts to this: we want to get from storage and save to storage. Since we're doing JSON parsing, it's usually a good idea to put these values in a try / catch.

New file: utils/storage.ts
import AsyncStorage from "@react-native-async-storage/async-storage";

export async function getFromStorage(key: string) {
try {
const data = await AsyncStorage.getItem(key);
return data ? JSON.parse(data) : null;
} catch {
return null;
}
}

export async function saveToStorage(key: string, data: object) {
try {
await AsyncStorage.setItem(key, JSON.stringify(data));
} catch {}
}

Fetch initial data

Use the useEffect hook fetch the initial data - once - when the app launches.

Update: app/index.tsx
@@ -1,7 +1,10 @@
import { StyleSheet, TextInput, FlatList, View, Text } from "react-native";
import { theme } from "../theme";
import { ShoppingListItem } from "../components/ShoppingListItem";
-import { useState } from "react";
+import { useEffect, useState } from "react";
+import { getFromStorage } from "../utils/storage";
+
+const storageKey = "shopping-list";

type ShoppingListItemType = {
id: string;
@@ -14,6 +17,17 @@ export default function App() {
const [shoppingList, setShoppingList] = useState<ShoppingListItemType[]>([]);
const [value, setValue] = useState<string>();

+ useEffect(() => {
+ const fetchInitial = async () => {
+ const data = await getFromStorage(storageKey);
+ if (data) {
+ setShoppingList(data);
+ }
+ };
+
+ fetchInitial();
+ }, []);
+
const handleSubmit = () => {
if (value) {
const newShoppingList = [

Sync updates

And update the shopping list in storage whenever an item is modified (deleted or update)

Update: app/index.tsx
@@ -2,7 +2,7 @@ import { StyleSheet, TextInput, FlatList, View, Text } from "react-native";
import { theme } from "../theme";
import { ShoppingListItem } from "../components/ShoppingListItem";
import { useEffect, useState } from "react";
-import { getFromStorage } from "../utils/storage";
+import { getFromStorage, saveToStorage } from "../utils/storage";

const storageKey = "shopping-list";

@@ -39,6 +39,7 @@ export default function App() {
...shoppingList,
];
setShoppingList(newShoppingList);
+ saveToStorage(storageKey, newShoppingList);
setValue(undefined);
}
};
@@ -62,6 +63,7 @@ export default function App() {
return item;
}
});
+ saveToStorage(storageKey, newShoppingList);
setShoppingList(newShoppingList);
};
Using libraries

As your application grows, you'd usually end up reaching for a state management library instead of managing this data storage step yourself. Most JavaScript state management libraries work in React Native, and they almost always include an adapter of sorts that allows you to use Async Storage to persist the data offline.