Bird
Raised Fist0
React Nativemobile~20 mins

SQLite with expo-sqlite in React Native - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Notes List
A simple notes app screen that stores notes locally using SQLite with expo-sqlite. Users can add new notes and see the list of saved notes.
Target UI
-------------------------
| Notes List            |
|-----------------------|
| + Add Note            |
|-----------------------|
| - Note 1              |
| - Note 2              |
| - Note 3              |
|                       |
-------------------------
Use expo-sqlite to create a local database and a table for notes.
Display a list of notes stored in the database.
Add a button labeled '+ Add Note' that opens a prompt to enter a new note.
Save the new note to the database and update the list immediately.
Handle database opening, table creation, insertion, and querying.
Starter Code
React Native
import React, { useEffect, useState } from 'react';
import { View, Text, Button, FlatList, Alert, TextInput, Modal, StyleSheet } from 'react-native';
import * as SQLite from 'expo-sqlite';

const db = SQLite.openDatabase('notes.db');

export default function NotesList() {
  const [notes, setNotes] = useState([]);
  const [modalVisible, setModalVisible] = useState(false);
  const [newNote, setNewNote] = useState('');

  useEffect(() => {
    // TODO: Create notes table if not exists
    // TODO: Load notes from database
  }, []);

  const addNote = () => {
    // TODO: Insert newNote into database
    // TODO: Refresh notes list
    // TODO: Close modal and clear input
  };

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Button title='+ Add Note' onPress={() => setModalVisible(true)} />
      <FlatList
        data={notes}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => <Text style={{ padding: 10 }}>- {item.text}</Text>}
      />

      <Modal visible={modalVisible} animationType='slide' transparent={true}>
        <View style={styles.modalView}>
          <TextInput
            placeholder='Enter note'
            value={newNote}
            onChangeText={setNewNote}
            style={styles.input}
          />
          <Button title='Save' onPress={addNote} />
          <Button title='Cancel' onPress={() => setModalVisible(false)} />
        </View>
      </Modal>
    </View>
  );
}

const styles = StyleSheet.create({
  modalView: {
    margin: 20,
    backgroundColor: 'white',
    borderRadius: 8,
    padding: 20,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.25,
    shadowRadius: 4,
    elevation: 5,
    flex: 1,
    justifyContent: 'center'
  },
  input: {
    borderWidth: 1,
    borderColor: '#ccc',
    padding: 10,
    marginBottom: 10,
    borderRadius: 4
  }
});
Task 1
Task 2
Task 3
Solution
React Native
import React, { useEffect, useState } from 'react';
import { View, Text, Button, FlatList, TextInput, Modal, StyleSheet } from 'react-native';
import * as SQLite from 'expo-sqlite';

const db = SQLite.openDatabase('notes.db');

export default function NotesList() {
  const [notes, setNotes] = useState([]);
  const [modalVisible, setModalVisible] = useState(false);
  const [newNote, setNewNote] = useState('');

  useEffect(() => {
    db.transaction(tx => {
      tx.executeSql(
        'CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT);'
      );
    }, null, loadNotes);
  }, []);

  const loadNotes = () => {
    db.transaction(tx => {
      tx.executeSql(
        'SELECT * FROM notes;',
        [],
        (_, { rows }) => {
          setNotes(rows._array);
        }
      );
    });
  };

  const addNote = () => {
    if (newNote.trim() === '') return;
    db.transaction(tx => {
      tx.executeSql(
        'INSERT INTO notes (text) VALUES (?);',
        [newNote],
        () => {
          loadNotes();
          setModalVisible(false);
          setNewNote('');
        }
      );
    });
  };

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Button title='+ Add Note' onPress={() => setModalVisible(true)} />
      <FlatList
        data={notes}
        keyExtractor={(item) => item.id.toString()}
        renderItem={({ item }) => <Text style={{ padding: 10 }}>- {item.text}</Text>}
      />

      <Modal visible={modalVisible} animationType='slide' transparent={true}>
        <View style={styles.modalView}>
          <TextInput
            placeholder='Enter note'
            value={newNote}
            onChangeText={setNewNote}
            style={styles.input}
            accessibilityLabel='Note input field'
          />
          <Button title='Save' onPress={addNote} accessibilityLabel='Save note button' />
          <Button title='Cancel' onPress={() => setModalVisible(false)} accessibilityLabel='Cancel button' />
        </View>
      </Modal>
    </View>
  );
}

const styles = StyleSheet.create({
  modalView: {
    margin: 20,
    backgroundColor: 'white',
    borderRadius: 8,
    padding: 20,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.25,
    shadowRadius: 4,
    elevation: 5,
    flex: 1,
    justifyContent: 'center'
  },
  input: {
    borderWidth: 1,
    borderColor: '#ccc',
    padding: 10,
    marginBottom: 10,
    borderRadius: 4
  }
});

This app uses expo-sqlite to create a local database named notes.db. On first load, it creates a table called notes if it does not exist. Then it loads all saved notes into state and displays them in a list.

The '+ Add Note' button opens a modal with a text input. When the user types a note and taps 'Save', the note is inserted into the database. After insertion, the app reloads the notes list to show the new note immediately. The modal closes and the input clears.

Accessibility labels are added for better screen reader support. The UI uses simple components and styles for clarity and ease of use.

Final Result
Completed Screen
-------------------------
| Notes List            |
|-----------------------|
| + Add Note            |
|-----------------------|
| - Buy groceries       |
| - Call mom            |
| - Read a book         |
|                       |
-------------------------
User taps '+ Add Note' button to open a modal input.
User types a note and taps 'Save' to add it to the list.
The new note appears immediately in the list below.
User can tap 'Cancel' to close the modal without saving.
Stretch Goal
Add the ability to delete a note by tapping on it with a confirmation alert.
💡 Hint
Use a TouchableOpacity wrapping each note text. On press, show Alert.alert with Yes/No options. If Yes, delete the note from the database and reload the list.

Practice

(1/5)
1. What is the main purpose of using expo-sqlite in a React Native app?
easy
A. To store and manage data locally using a small database
B. To fetch data from a remote server
C. To style the app interface
D. To handle user authentication

Solution

  1. Step 1: Understand expo-sqlite functionality

    expo-sqlite provides a way to store data locally on the device using a SQLite database.
  2. Step 2: Compare options with expo-sqlite purpose

    Only To store and manage data locally using a small database describes local data storage with a database, which matches expo-sqlite's role.
  3. Final Answer:

    To store and manage data locally using a small database -> Option A
  4. Quick Check:

    expo-sqlite = local database storage [OK]
Hint: expo-sqlite is for local data storage, not remote or styling [OK]
Common Mistakes:
  • Confusing expo-sqlite with network data fetching
  • Thinking expo-sqlite handles UI styling
  • Assuming expo-sqlite manages user login
2. Which of the following is the correct way to open a database using expo-sqlite?
easy
A. const db = SQLite.newDatabase('mydb.db');
B. const db = SQLite.createDatabase('mydb.db');
C. const db = SQLite.connect('mydb.db');
D. const db = SQLite.openDatabase('mydb.db');

Solution

  1. Step 1: Recall expo-sqlite database opening method

    The correct method to open or create a database is openDatabase.
  2. Step 2: Match method names with options

    Only const db = SQLite.openDatabase('mydb.db'); uses openDatabase, which is the correct syntax.
  3. Final Answer:

    const db = SQLite.openDatabase('mydb.db'); -> Option D
  4. Quick Check:

    openDatabase() opens SQLite DB [OK]
Hint: Use openDatabase() to open or create SQLite DB [OK]
Common Mistakes:
  • Using createDatabase() which does not exist
  • Using connect() or newDatabase() which are invalid
  • Missing the correct method name openDatabase()
3. Given this code snippet, what will be logged to the console?
const db = SQLite.openDatabase('test.db');
db.transaction(tx => {
  tx.executeSql('CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY NOT NULL, name TEXT);');
  tx.executeSql('INSERT INTO items (name) VALUES (?)', ['apple']);
  tx.executeSql('SELECT * FROM items;', [], (_, { rows }) => {
    console.log(rows._array);
  });
});
medium
A. undefined
B. [{ id: 1, name: 'apple' }]
C. []
D. SyntaxError

Solution

  1. Step 1: Understand the SQL commands executed

    The code creates a table if missing, inserts one row with name 'apple', then selects all rows.
  2. Step 2: Analyze the SELECT query result

    The SELECT returns one row with id 1 and name 'apple', logged as an array of objects.
  3. Final Answer:

    [{ id: 1, name: 'apple' }] -> Option B
  4. Quick Check:

    Inserted row appears in SELECT result [OK]
Hint: Inserted rows appear in SELECT query result array [OK]
Common Mistakes:
  • Expecting empty array before insertion
  • Confusing rows._array with undefined
  • Assuming syntax error from correct SQL
4. What is wrong with this code snippet that tries to insert data into a SQLite table?
db.transaction(tx => {
  tx.executeSql('INSERT INTO users (name, age) VALUES (?, ?)', ['John']);
});
medium
A. The SQL statement has fewer values than placeholders
B. The table name is incorrect
C. The transaction method is missing a callback
D. The executeSql method cannot use placeholders

Solution

  1. Step 1: Check SQL placeholders and values

    The SQL has two placeholders (?, ?) but only one value 'John' is provided.
  2. Step 2: Identify mismatch causing error

    Mismatch between placeholders and values causes an error during execution.
  3. Final Answer:

    The SQL statement has fewer values than placeholders -> Option A
  4. Quick Check:

    Placeholders must match values count [OK]
Hint: Match number of ? placeholders with values array length [OK]
Common Mistakes:
  • Ignoring mismatch between placeholders and values
  • Assuming table name is wrong without checking
  • Thinking executeSql cannot use placeholders
5. You want to display a list of items stored in a SQLite table inside your React Native app. Which approach correctly updates the UI after fetching data from the database?
const [items, setItems] = useState([]);

useEffect(() => {
  db.transaction(tx => {
    tx.executeSql('SELECT * FROM items;', [], (_, { rows }) => {
      // What should go here?
    });
  });
}, []);
hard
A. items = rows._array;
B. return rows._array;
C. setItems(rows._array);
D. console.log(rows._array);

Solution

  1. Step 1: Understand React state update

    To update UI, React state setter setItems must be called with new data.
  2. Step 2: Identify correct way to update state

    Assigning directly to items or returning data won't update state; logging only shows data.
  3. Final Answer:

    setItems(rows._array); -> Option C
  4. Quick Check:

    Use setItems() to update state and UI [OK]
Hint: Use setState function to update React state after DB fetch [OK]
Common Mistakes:
  • Assigning directly to state variable instead of using setter
  • Returning data inside executeSql callback
  • Only logging data without updating state
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard