Bird
Raised Fist0
React Nativemobile~20 mins

Realtime Database in React Native - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Realtime Chat
A simple chat screen that shows messages from a realtime database and allows sending new messages.
Target UI
-------------------------
| Realtime Chat         |
-------------------------
| [Message list here]   |
|                       |
| --------------------- |
| | Type message...   | |
| | [Send Button]     | |
-------------------------
Display a list of chat messages from a realtime database.
Listen for new messages and update the list in real time.
Provide a text input to type a new message.
Send button adds the message to the database and clears input.
Use Firebase Realtime Database for data storage.
Starter Code
React Native
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, Button, FlatList, StyleSheet } from 'react-native';
import database from '@react-native-firebase/database';

export default function RealtimeChat() {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState('');

  useEffect(() => {
    // TODO: Add listener to fetch messages from realtime database
  }, []);

  const sendMessage = () => {
    // TODO: Add code to send message to realtime database
  };

  return (
    <View style={styles.container}>
      <FlatList
        data={messages}
        keyExtractor={item => item.id}
        renderItem={({ item }) => <Text style={styles.message}>{item.text}</Text>}
      />
      <View style={styles.inputContainer}>
        <TextInput
          style={styles.input}
          placeholder="Type message..."
          value={input}
          onChangeText={setInput}
        />
        <Button title="Send" onPress={sendMessage} />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 16 },
  message: { padding: 8, fontSize: 16 },
  inputContainer: { flexDirection: 'row', alignItems: 'center' },
  input: { flex: 1, borderColor: '#ccc', borderWidth: 1, padding: 8, marginRight: 8, borderRadius: 4 }
});
Task 1
Task 2
Solution
React Native
import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, Button, FlatList, StyleSheet } from 'react-native';
import database from '@react-native-firebase/database';

export default function RealtimeChat() {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState('');

  useEffect(() => {
    const messagesRef = database().ref('/messages');
    const onValueChange = messagesRef.on('value', snapshot => {
      const data = snapshot.val() || {};
      const parsedMessages = Object.entries(data).map(([id, value]) => ({ id, text: value.text }));
      setMessages(parsedMessages);
    });
    return () => messagesRef.off('value', onValueChange);
  }, []);

  const sendMessage = () => {
    if (input.trim().length === 0) return;
    const messagesRef = database().ref('/messages');
    messagesRef.push({ text: input.trim() });
    setInput('');
  };

  return (
    <View style={styles.container}>
      <FlatList
        data={messages}
        keyExtractor={item => item.id}
        renderItem={({ item }) => <Text style={styles.message}>{item.text}</Text>}
      />
      <View style={styles.inputContainer}>
        <TextInput
          style={styles.input}
          placeholder="Type message..."
          value={input}
          onChangeText={setInput}
          accessibilityLabel="Message input"
          accessible={true}
        />
        <Button title="Send" onPress={sendMessage} accessibilityLabel="Send message button" />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 16, backgroundColor: '#fff' },
  message: { padding: 8, fontSize: 16, borderBottomColor: '#eee', borderBottomWidth: 1 },
  inputContainer: { flexDirection: 'row', alignItems: 'center', marginTop: 8 },
  input: { flex: 1, borderColor: '#ccc', borderWidth: 1, padding: 8, marginRight: 8, borderRadius: 4 }
});

This React Native component connects to Firebase Realtime Database to show a live chat.

Inside useEffect, it sets a listener on the /messages path. When data changes, it updates the messages state with the latest messages.

The sendMessage function pushes a new message to the database and clears the input field.

Accessibility labels are added for screen readers. The UI uses a FlatList to efficiently render messages and a text input with a send button below.

Final Result
Completed Screen
-------------------------
| Realtime Chat         |
-------------------------
| Hello!                |
| How are you?          |
| I'm fine, thanks!     |
|                       |
| --------------------- |
| | Type message...   | |
| | [Send Button]     | |
-------------------------
When the app loads, it shows all messages from the database.
If a new message is added by anyone, the list updates immediately.
Typing in the input and pressing Send adds the message to the chat and clears the input.
Stretch Goal
Add a loading indicator while messages are loading from the database.
💡 Hint
Use a state variable to track loading and show an ActivityIndicator component until messages load.

Practice

(1/5)
1. What is the main purpose of using on('value') in React Native with Realtime Database?
easy
A. To write new data to the database
B. To delete data from the database
C. To listen for live updates to data changes
D. To stop listening to data changes

Solution

  1. Step 1: Understand the on('value') method

    This method sets up a listener that triggers every time the data at the reference changes.
  2. Step 2: Identify the purpose in app behavior

    It keeps the app data synced live by updating the UI or state whenever data changes.
  3. Final Answer:

    To listen for live updates to data changes -> Option C
  4. Quick Check:

    on('value') listens for updates [OK]
Hint: Remember: 'on' means listen for live data changes [OK]
Common Mistakes:
  • Confusing 'on' with writing data
  • Thinking it deletes data
  • Assuming it stops listeners
2. Which of the following is the correct way to create a reference to the 'users' node in Firebase Realtime Database in React Native?
easy
A. const usersRef = database().ref('users');
B. const usersRef = database.ref('users');
C. const usersRef = database().reference('users');
D. const usersRef = database().child('users');

Solution

  1. Step 1: Recall the correct method to get a reference

    In React Native Firebase, database() returns the database instance, and ref('path') creates a reference.
  2. Step 2: Check each option's syntax

    const usersRef = database().ref('users'); uses database().ref('users') which is correct. const usersRef = database.ref('users'); misses parentheses after database. const usersRef = database().reference('users'); uses a wrong method name. const usersRef = database().child('users'); uses child which is valid but requires a reference first.
  3. Final Answer:

    const usersRef = database().ref('users'); -> Option A
  4. Quick Check:

    Use database().ref('path') for references [OK]
Hint: Use parentheses after database and then call ref() [OK]
Common Mistakes:
  • Omitting parentheses after database
  • Using wrong method names like reference()
  • Confusing ref() with child() without base ref
3. Given the code below, what will be logged when the data at 'messages' changes?
const messagesRef = database().ref('messages');
messagesRef.on('value', snapshot => {
  console.log(snapshot.val());
});
medium
A. An error message because 'val()' is invalid
B. The current value of 'messages' node as a JavaScript object
C. A function reference to the listener
D. Nothing, because 'on' does not trigger automatically

Solution

  1. Step 1: Understand what snapshot.val() returns

    It returns the current data stored at the reference as a JavaScript object or value.
  2. Step 2: Analyze the listener behavior

    The on('value') listener triggers immediately with current data and on every change, so it logs the current 'messages' data.
  3. Final Answer:

    The current value of 'messages' node as a JavaScript object -> Option B
  4. Quick Check:

    snapshot.val() returns data [OK]
Hint: Remember: snapshot.val() gives current data object [OK]
Common Mistakes:
  • Thinking on() returns a function
  • Believing val() is invalid
  • Assuming on() does not trigger immediately
4. What is wrong with the following React Native code snippet using Realtime Database?
const ref = database().ref('items');
ref.on('value', snapshot => {
  setItems(snapshot.val());
});

// Later in component unmount
ref.off('value');
medium
A. ref.off('value') should be called before on('value')
B. The ref is incorrectly created without parentheses
C. setItems cannot be called inside on('value')
D. The listener is not properly removed because off() needs the callback function

Solution

  1. Step 1: Understand how to remove listeners

    To remove a specific listener, off('value', callback) must be called with the same callback function used in on.
  2. Step 2: Identify the problem in the code

    The code calls off('value') without the callback, so the listener remains active, causing potential memory leaks.
  3. Final Answer:

    The listener is not properly removed because off() needs the callback function -> Option D
  4. Quick Check:

    off() requires callback to remove listener [OK]
Hint: Always pass the same callback to off() to stop listeners [OK]
Common Mistakes:
  • Calling off() without callback removes nothing
  • Confusing ref creation syntax
  • Thinking setItems can't be called inside listener
5. You want to display a live list of chat messages in your React Native app using Realtime Database. Which approach correctly ensures the listener is cleaned up to avoid memory leaks?
hard
A. Use useEffect hook to attach on('value') listener and return a cleanup function that calls off('value', callback)
B. Attach on('value') listener outside the component and never remove it
C. Call off('value') immediately after on('value') inside the same function
D. Use once('value') instead of on('value') to get live updates

Solution

  1. Step 1: Understand React Native lifecycle with listeners

    Listeners should be attached when component mounts and removed when it unmounts to avoid leaks.
  2. Step 2: Use useEffect for setup and cleanup

    Inside useEffect, attach the listener and return a cleanup function that calls off with the same callback.
  3. Step 3: Evaluate other options

    Attach on('value') listener outside the component and never remove it leaks memory. Call off('value') immediately after on('value') inside the same function removes listener immediately, so no updates. Use once('value') instead of on('value') to get live updates only fetches once, not live updates.
  4. Final Answer:

    Use useEffect hook to attach on('value') listener and return a cleanup function that calls off('value', callback) -> Option A
  5. Quick Check:

    Attach listeners in useEffect and clean up with off() [OK]
Hint: Attach listeners in useEffect and clean up on unmount [OK]
Common Mistakes:
  • Not removing listeners causing memory leaks
  • Removing listener immediately after adding
  • Using once() instead of on() for live data
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