Bird
Raised Fist0
React Nativemobile~20 mins

Firestore CRUD operations in React Native - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Firestore CRUD Demo
A simple React Native screen to create, read, update, and delete items in Firestore.
Target UI
-------------------------
| Firestore CRUD Demo    |
-------------------------
| [Input: New Item]      |
| [Add Item Button]      |
|-----------------------|
| Items List:            |
| 1. Item 1 [Edit][Del] |
| 2. Item 2 [Edit][Del] |
| ...                   |
-------------------------
TextInput to enter new item text
Button to add new item to Firestore
Display list of items from Firestore in real-time
Each item shows text with Edit and Delete buttons
Edit button allows changing item text inline
Delete button removes item from Firestore
Starter Code
React Native
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, Button, FlatList, TouchableOpacity } from 'react-native';
import firestore from '@react-native-firebase/firestore';

export default function FirestoreCRUD() {
  const [newItem, setNewItem] = useState('');
  const [items, setItems] = useState([]);

  useEffect(() => {
    // TODO: Subscribe to Firestore collection and update items state
  }, []);

  const addItem = () => {
    // TODO: Add new item to Firestore
  };

  const deleteItem = (id) => {
    // TODO: Delete item from Firestore by id
  };

  const editItem = (id, newText) => {
    // TODO: Update item text in Firestore
  };

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Text style={{ fontSize: 24, marginBottom: 10 }}>Firestore CRUD Demo</Text>
      <TextInput
        placeholder="New Item"
        value={newItem}
        onChangeText={setNewItem}
        style={{ borderWidth: 1, padding: 8, marginBottom: 10 }}
      />
      <Button title="Add Item" onPress={addItem} />
      <FlatList
        data={items}
        keyExtractor={item => item.id}
        renderItem={({ item }) => (
          <View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 10 }}>
            <Text>{item.text}</Text>
            <View style={{ flexDirection: 'row' }}>
              <TouchableOpacity onPress={() => {/* TODO: Trigger edit */}}>
                <Text style={{ color: 'blue', marginRight: 10 }}>Edit</Text>
              </TouchableOpacity>
              <TouchableOpacity onPress={() => deleteItem(item.id)}>
                <Text style={{ color: 'red' }}>Delete</Text>
              </TouchableOpacity>
            </View>
          </View>
        )}
      />
    </View>
  );
}
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
React Native
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, Button, FlatList, TouchableOpacity } from 'react-native';
import firestore from '@react-native-firebase/firestore';

export default function FirestoreCRUD() {
  const [newItem, setNewItem] = useState('');
  const [items, setItems] = useState([]);
  const [editingId, setEditingId] = useState(null);
  const [editingText, setEditingText] = useState('');

  useEffect(() => {
    const unsubscribe = firestore()
      .collection('items')
      .orderBy('createdAt', 'desc')
      .onSnapshot(querySnapshot => {
        const list = [];
        querySnapshot.forEach(doc => {
          list.push({ id: doc.id, ...doc.data() });
        });
        setItems(list);
      });
    return () => unsubscribe();
  }, []);

  const addItem = () => {
    if (newItem.trim() === '') return;
    firestore()
      .collection('items')
      .add({ text: newItem.trim(), createdAt: firestore.FieldValue.serverTimestamp() });
    setNewItem('');
  };

  const deleteItem = (id) => {
    firestore().collection('items').doc(id).delete();
  };

  const startEditing = (id, text) => {
    setEditingId(id);
    setEditingText(text);
  };

  const saveEdit = (id) => {
    if (editingText.trim() === '') return;
    firestore().collection('items').doc(id).update({ text: editingText.trim() });
    setEditingId(null);
    setEditingText('');
  };

  const cancelEdit = () => {
    setEditingId(null);
    setEditingText('');
  };

  return (
    <View style={{ flex: 1, padding: 20 }}>
      <Text style={{ fontSize: 24, marginBottom: 10 }}>Firestore CRUD Demo</Text>
      <TextInput
        placeholder="New Item"
        value={newItem}
        onChangeText={setNewItem}
        style={{ borderWidth: 1, padding: 8, marginBottom: 10 }}
        accessibilityLabel="New item input"
      />
      <Button title="Add Item" onPress={addItem} accessibilityLabel="Add item button" />
      <FlatList
        data={items}
        keyExtractor={item => item.id}
        renderItem={({ item }) => (
          <View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 10, alignItems: 'center' }}>
            {editingId === item.id ? (
              <>
                <TextInput
                  value={editingText}
                  onChangeText={setEditingText}
                  style={{ borderWidth: 1, padding: 4, flex: 1, marginRight: 10 }}
                  accessibilityLabel={`Edit input for ${item.text}`}
                />
                <Button title="Save" onPress={() => saveEdit(item.id)} accessibilityLabel="Save edit button" />
                <Button title="Cancel" onPress={cancelEdit} accessibilityLabel="Cancel edit button" />
              </>
            ) : (
              <>
                <Text style={{ flex: 1 }}>{item.text}</Text>
                <TouchableOpacity onPress={() => startEditing(item.id, item.text)} accessibilityRole="button" accessibilityLabel={`Edit button for ${item.text}`}>
                  <Text style={{ color: 'blue', marginRight: 10 }}>Edit</Text>
                </TouchableOpacity>
                <TouchableOpacity onPress={() => deleteItem(item.id)} accessibilityRole="button" accessibilityLabel={`Delete button for ${item.text}`}>
                  <Text style={{ color: 'red' }}>Delete</Text>
                </TouchableOpacity>
              </>
            )}
          </View>
        )}
      />
    </View>
  );
}

This React Native component connects to Firestore to perform CRUD operations on a collection named 'items'.

  • We use useEffect to subscribe to real-time updates from Firestore and update the items state.
  • addItem adds a new document with the entered text and a timestamp.
  • deleteItem removes a document by its ID.
  • Editing is handled by tracking the editingId and editingText. When editing, an input and Save/Cancel buttons appear inline.
  • Accessibility labels are added for better screen reader support.
  • The UI updates live as Firestore data changes.

This approach gives a simple, clear example of Firestore CRUD in React Native.

Final Result
Completed Screen
-------------------------
| Firestore CRUD Demo    |
-------------------------
| [Input: New Item]      |
| [Add Item Button]      |
|-----------------------|
| Items List:            |
| 1. Item 1 [Edit][Del] |
| 2. Item 2 [Edit][Del] |
| ...                   |
-------------------------
User types text in the input and taps 'Add Item' to save it to Firestore.
Items list updates live showing all saved items.
Tapping 'Edit' replaces item text with an input and shows 'Save' and 'Cancel' buttons.
Tapping 'Save' updates the item text in Firestore and returns to normal view.
Tapping 'Cancel' discards changes and returns to normal view.
Tapping 'Delete' removes the item from Firestore and the list updates.
Stretch Goal
Add a pull-to-refresh feature to reload the items list manually.
💡 Hint
Use FlatList's refreshing and onRefresh props to implement pull-to-refresh.

Practice

(1/5)
1. What does the collection() function do in Firestore when used in React Native?
easy
A. It points to a group of documents in the database.
B. It creates a new document with data.
C. It deletes a document from the database.
D. It updates fields inside a document.

Solution

  1. Step 1: Understand Firestore data structure

    Firestore organizes data in collections which contain documents.
  2. Step 2: Role of collection()

    The collection() function points to a specific collection (group of documents) in Firestore.
  3. Final Answer:

    It points to a group of documents in the database. -> Option A
  4. Quick Check:

    collection() = points to documents group [OK]
Hint: Remember: collection() targets groups, doc() targets single items [OK]
Common Mistakes:
  • Confusing collection() with doc()
  • Thinking collection() creates data
  • Mixing update and delete operations
2. Which of the following is the correct syntax to add a new document with data to a Firestore collection named 'users'?
easy
A. await addDoc(collection(db, 'users'), {name: 'Anna', age: 25});
B. await db.collection('users').add({name: 'Anna', age: 25});
C. await setDoc(doc(db, 'users'), {name: 'Anna', age: 25});
D. await updateDoc(collection(db, 'users'), {name: 'Anna', age: 25});

Solution

  1. Step 1: Identify correct Firestore add syntax in React Native

    The addDoc function adds a new document to a collection.
  2. Step 2: Check the syntax correctness

    await addDoc(collection(db, 'users'), {name: 'Anna', age: 25}); uses addDoc(collection(db, 'users'), data) which is the correct modern syntax.
  3. Final Answer:

    await addDoc(collection(db, 'users'), {name: 'Anna', age: 25}); -> Option A
  4. Quick Check:

    Add new doc = addDoc(collection(db, 'name'), data) [OK]
Hint: Use addDoc with collection() to add new documents [OK]
Common Mistakes:
  • Using setDoc without doc() reference
  • Calling updateDoc on a collection instead of a document
  • Using deprecated syntax from older SDK versions
3. Given this code snippet, what will be logged to the console?
const docRef = doc(db, 'tasks', 'task1');
await updateDoc(docRef, {completed: true});
const docSnap = await getDoc(docRef);
console.log(docSnap.data());
medium
A. { completed: false }
B. { completed: true }
C. An error because updateDoc cannot be awaited
D. undefined

Solution

  1. Step 1: Understand updateDoc effect

    The updateDoc call sets the field completed to true in the document 'task1'.
  2. Step 2: Reading updated document data

    The getDoc fetches the updated document, so docSnap.data() returns the updated fields.
  3. Final Answer:

    { completed: true } -> Option B
  4. Quick Check:

    updateDoc changes data, getDoc reads updated data [OK]
Hint: updateDoc changes data; getDoc reads fresh data [OK]
Common Mistakes:
  • Assuming updateDoc returns data
  • Forgetting to await async calls
  • Expecting old data without re-fetching
4. Identify the error in this Firestore delete operation code:
const docRef = doc(db, 'notes', 'note1');
await deleteDoc(collection(db, 'notes', 'note1'));
medium
A. The collection path is missing a document ID.
B. doc() cannot be used to get a document reference.
C. deleteDoc must be called without await.
D. deleteDoc should receive a document reference, not a collection.

Solution

  1. Step 1: Check deleteDoc argument type

    The deleteDoc function requires a document reference, not a collection reference.
  2. Step 2: Analyze the code's argument

    The code passes collection(db, 'notes', 'note1') which is invalid because collection() does not accept a document ID.
  3. Final Answer:

    deleteDoc should receive a document reference, not a collection. -> Option D
  4. Quick Check:

    deleteDoc(docRef) not deleteDoc(collection(...)) [OK]
Hint: deleteDoc needs doc(), not collection() [OK]
Common Mistakes:
  • Passing collection() instead of doc() to deleteDoc
  • Misunderstanding collection() parameters
  • Omitting await on async calls
5. You want to update a user's profile in Firestore only if the 'age' field exists and is less than 30. Which code snippet correctly performs this conditional update?
hard
A. const docRef = doc(db, 'users', 'user1'); await updateDoc(docRef, {status: 'young'}); const docSnap = await getDoc(docRef); if (docSnap.data().age >= 30) { await deleteDoc(docRef); }
B. await updateDoc(doc(db, 'users', 'user1'), {status: 'young', age: 30});
C. const docRef = doc(db, 'users', 'user1'); const docSnap = await getDoc(docRef); if (docSnap.exists() && docSnap.data().age < 30) { await updateDoc(docRef, {status: 'young'}); }
D. const docRef = doc(db, 'users', 'user1'); await setDoc(docRef, {status: 'young'}, {merge: true});

Solution

  1. Step 1: Check for field existence and condition

    Use getDoc to read the document and verify if 'age' exists and is less than 30.
  2. Step 2: Conditionally update document

    If condition is true, call updateDoc to update the 'status' field without overwriting other data.
  3. Final Answer:

    Code snippet that reads, checks, then updates conditionally. -> Option C
  4. Quick Check:

    Read first, then update if condition true [OK]
Hint: Always read document before conditional update [OK]
Common Mistakes:
  • Updating without checking field existence
  • Using setDoc without merge causing overwrite
  • Deleting document incorrectly after update
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