Step-by-Step Guide to Building a Mobile App in React Native with Expo Framework

Introduction

React Native is an open-source framework used to create cross-platform mobile apps. Working with React Native requires knowledge of JavaScript and React, and from there, it is a smooth learning curve into cross-platform mobile app development.

Expo is a framework used to create Android, iOS, and Web from one code base. It has a variety of useful SDK modules because you don't have to create your own from scratch.

In this tutorial, we shall learn how to create a Notes App and see what the Expo framework has to offer.

Setting Up the App

Ensure you have the following tools installed on your machine:

After that, install the Expo CLI to see the available commands:

npm install --global expo-cli

Creating a New App

To create a new project, use the following command:

#creates a new app in your prefered directory
npx create-expo-app notes-app

#changes the directory into the app folder
cd notes-app

To see the newly created app in action use the following command:

npx expo start

This will start the development server, and you can view the app on all three platforms, iOS, Android, and Web.

For iOS, if you have an iPhone, scan the QR code that has been generated in the terminal.

For Android, download the Expo Go App from Playstore and scan the QR code.

For the web, use the following command to install react native and react-dom to enable the setup of the development server on your local machine.

npx expo install react-native-web@~0.18.10 react-dom@18.2.0 @expo/webpack-config@^18.0.1

After that, you should see a screen with the words, " Open up App.js to start working on your app!"

Simple Note-Taking Application

We shall build a simple note-taking application. We shall make use of Async Storage since we are not using any backend to store the notes.

Import the following:


import React, { useState, useEffect } from 'react';
//these are components for enabling styling for the screen
import { StyleSheet, View,Text,TextInput,TouchableOpacity, FlatList, Alert } from 'react-native';

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

We import React to make use of the library within React Native just as we would in React apps.

useState is a hook that keeps track of the state within a component's function.

useEffect is a hook used to carry out side-effects such as fetching data which we shall use to fetch notes.

AyncStorage is a persistent, key-value storage system used as an alternative to LocalStorage.

App Component

We shall build our screen within this function component.

const App = () => {
  const [notes, setNotes] = useState([]);
  const [newNote, setNewNote] = useState('');

  useEffect(() => {
    // load saved notes on app start
    loadNotes();
  }, []);

Within our App component, begin by initializing the state that will keep track of the notes we create and delete. The 'notes' is the current state and setNotes is used to update the state.

The useEffect hook is used to fetch the notes we create, it takes in a function argument loadNotes() which we shall create shortly.

Saving the Notes

We create a function to save the notes the user creates. First, we create an if statement to check if the input field has any data and alert the user to type some data. This helps prevent data validation errors.

We create a note object with an id (for identification of a specific note) and the text the user keyed in. Then we proceed to create try-catch blocks to save the data and catch any errors that may be encountered.

const saveNote = async () => {
    if (newNote.trim() === '') {
      Alert.alert('Error', 'Note cannot be empty!');
      return;
    }

    // create a new note object
    const note = {
      id: Date.now().toString(),
      text: newNote,
    };

    // save the note to AsyncStorage
    try {
      await AsyncStorage.setItem(note.id, JSON.stringify(note));
      setNotes([...notes, note]);
      setNewNote('');
    } catch (error) {
      console.log(error);
    }
  };

Deleting the Notes

We create a function that asynchronously loads data from Asyncstorage. As mentioned before AsyncStorage uses a key and value system to store data, hence the need to get all keys to access the notes. Next, we retrieve the saved notes and update the state to reflect the notes we have retrieved.

All this is within try-catch blocks to handle any errors we may have as we delete the notes.

 const deleteNote = async (id) => {
    // remove the note from AsyncStorage
    try {
      await AsyncStorage.removeItem(id);
      setNotes(notes.filter((note) => note.id !== id));
    } catch (error) {
      console.log(error);
    }
  };

Loading the Notes

We create a function that asynchronously loads data from Asyncstorage. As mentioned before AsyncStorage uses a key and value system to store data, hence the need to get all keys to access the notes. Next, we retrieve the saved notes and update the state to reflect the notes we have retrieved.

All this is within try-catch blocks to handle any errors we may have as we get the notes.

  const loadNotes = async () => {
    // load all saved notes from AsyncStorage
    try {
      const keys = await AsyncStorage.getAllKeys();
      const savedNotes = await AsyncStorage.multiGet(keys);
      const notes = savedNotes.map((note) => JSON.parse(note[1]));
      setNotes(notes);
    } catch (error) {
      console.log(error);
    }
  };

Rendering the Notes

We create a function that takes in an item which shall be used to render the texts that the user keyed in as a note. It displays the note and comes with a delete button to delete the same note in the event the user does not need it anymore.


  const renderItem = ({ item }) => (
    <TouchableOpacity
      style={styles.noteItem}
      onPress={() => navigation.navigate('Note', { id: item.id })}
    >
      <Text style={styles.noteText}>{item.text}</Text>
      <TouchableOpacity onPress={() => deleteNote(item.id)}>
        <Text style={styles.deleteButtonText}>Delete</Text>
      </TouchableOpacity>
    </TouchableOpacity>
  );

Rendering the App

As with React apps, we need to render the things we see on the screen in the return section. Here we return a View, which wraps the items that will be displayed.

Here we create the Text Input field where the user will write the note in. Within it, we shall have a placeholder that guides the user on where to write the note. Next, a button that when click fires and saves the note the user wrote. Lastly, the notes will be displayed to the user in form of a list.

  return (
    <View style={styles.container}>
      <View style={styles.inputContainer}>
        <TextInput
          style={styles.textInput}
          placeholder="Enter a new note"
          value={newNote}
          onChangeText={setNewNote}
        />
        <TouchableOpacity style={styles.addButton} onPress={saveNote}>
          <Text style={styles.addButtonText}>Add</Text>
        </TouchableOpacity>
      </View>
      <FlatList
        data={notes}
        renderItem={renderItem}
        keyExtractor={(item) => item.id}
        contentContainerStyle={styles.listContainer}
      />
    </View>
  );
};
export default App;

Styling the Screen

The styling of the app is done by creating a styles object under the function component. It uses the StyleSheet we imported from React Native to style the screen.

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'flex-start',
    paddingTop: 50,
    paddingHorizontal: 20,
  },
  inputContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 20,
  },
  textInput: {
    flex: 1,
    height: 40,
    borderColor: '#ccc',
    borderWidth: 1,
    paddingHorizontal: 10,
    marginRight: 10,
  },
  addButton: {
    backgroundColor: '#3498db',
    paddingVertical: 10,
    paddingHorizontal: 20,
  }})

Conclusion

In this tutorial, we have built a very simple note-taking mobile application. In future cross-platform mobile tutorials, we shall learn how to create more robust apps with more features, screens, and connections to external services. See you soon.