Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Storing data: AsyncStorage

Right now tasks live only in state and disappear when the app is closed. AsyncStorage is a simple key-string store on the device, the counterpart of localStorage in the browser. The difference is that all its methods are asynchronous and return promises.

Installing

Go back to the todo project and install the package:

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

The package is included in Expo Go, so you do not need to rebuild the app; restarting npx expo start is enough.

Writing and reading

MethodWhat it does
AsyncStorage.setItem(key, value)Saves a string
AsyncStorage.getItem(key)Returns a string or null
AsyncStorage.removeItem(key)Removes a value

Only strings are stored, so the task array is turned into JSON. It is convenient to put this into a separate file:

// lib/storage.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import type { Task } from '@/components/TaskItem';
 
const KEY = 'tasks';
 
export async function loadTasks(): Promise<Task[]> {
  const json = await AsyncStorage.getItem(KEY);
  return json ? JSON.parse(json) : [];
}
 
export async function saveTasks(tasks: Task[]) {
  await AsyncStorage.setItem(KEY, JSON.stringify(tasks));
}

Create a lib folder in the project root. loadTasks returns an empty array if nothing has been saved yet.

Connecting it to the screen

On the main screen, load the tasks once when it opens and save them on every change:

// app/index.tsx
import { loadTasks, saveTasks } from '@/lib/storage';
 
const [tasks, setTasks] = useState<Task[]>([]);
const [loaded, setLoaded] = useState(false);
 
useEffect(() => {
  loadTasks().then((saved) => {
    setTasks(saved);
    setLoaded(true);
  });
}, []);
 
useEffect(() => {
  if (loaded) saveTasks(tasks);
}, [tasks, loaded]);

Add a couple of tasks, close the app completely and open it again: the tasks are still there. The loaded flag allows saving only after loading.

Where the data lives

AsyncStorage keeps data in a regular app file without encryption. Uninstalling the app erases it. It suits settings, drafts, caches and lists like ours. For large amounts of data and complex queries, use the expo-sqlite database.

Common mistake

Saving without the loaded flag:

// app/index.tsx
// Wrong:
useEffect(() => {
  saveTasks(tasks);
}, [tasks]);

On the first render tasks is an empty array, and the effect writes it to storage before loadTasks manages to read the old tasks. After every restart the list is empty. Save only after the data has been loaded.

Thanks for reading!

Donate to the site — we work hard for you.