This blog post has been updated for version >=6.0.0 of React Native Firebase.
Late last year, Firebase announced Cloud Firestore, a NoSQL document database that compliments the existing Realtime Database product. React Native Firebase has since provided support for using it on both Android & iOS in React Native, right from day one of the release.
Building a TODO app with Cloud Firestore & React Native #
Let's go ahead and start building a TODO app with Cloud Firestore & React Native Firebase.
Assuming you’ve integrated React Native Firebase and added
@react-native-firebase/firestore to your project (using these docs), we can get started.
Create a new file
Todos.js in the root of your React Native project and point your
index.ios.js files to it:
Next; set up a basic React component in
Create your Cloud Firestore data structure #
Cloud Firestore allows documents (objects of data) to be stored in collections (think of them as containers for your documents). Our TODO app will hold a list of todo documents within a single “todos” collection only for simplicity. Each document contains the data specific to that todo - in our case the
The first step is to create a reference to the collection, which can be used throughout our component to query it.
@react-native-firebase/firestore and create this reference in our component:
Create a user interface #
For simplicity, we'll use react-native-paper for our UI - a great library for React Native which provides pre-built React components that follow Googles Material Design guidelines. It's super easy to install, head over to their documentation on how to get started.
Let's now create a simple UI with a scrollable list of todos, along with a text input to add new ones:
You should now see the example scroll view, a text input, and a button which does nothing - something similar to the following:
We now need to connect the text input to our local state, so we can send the value to Cloud Firestore when the button is pressed; subsequently adding the new TODO item.
We'll use the
useState hook here, and update state every time the text changes via the
onChangeText prop from the
Modify our Todos component and add the new state item with an initial state of an empty string:
Then set the state item to be the
value of our
TextInput and the
onChangeText to call out
setTodo function; which will update our state whenever the user enters text into the input:
Your app should now respond to text changes, with the value reflecting local state:
Adding new TODOs #
To add a new document to the collection, we can call the
add method on our collection reference.
Create a new function in our component called
addTodo. This method will use our existing
ref variable to add a new item to the Firestore database.
Update our button
onPress to call this new
When the button is pressed, the new todo is sent to Cloud Firestore and added to the collection. We then reset the
todo state variable to clear the
TextInput box value.
add() method on the
CollectionReference is asynchronous and additionally returns the
DocumentReference for the newly created document.
If we check our collection on the Firebase Console we should now see our todo records being added to the collection:
If you're having problems adding new documents; make sure your Cloud Firestore Security Rules allow writing to todos collection.
Subscribe to collection updates #
Even though we’re populating the collection, we still want to display the documents on our app.
For reading documents; Cloud Firestore provides two ways;
get()queries the collection once
onSnapshot()allows subscribing to updates to the query results (e.g. when a document changes) in realtime
As we want to subscribe to updates we'll want to use
onSnapshot() so let's go ahead and setup some additional component state to handle the updates and the subscription.
todos state to the component. The
loading state will default to
true, and the
todos state will be an empty array:
We need a loading state to indicate to the user that the first connection (and initial data read) to Cloud Firestore has not yet completed.
useEffect hook we can trigger a function to be called when the component first mounts. By returning the
onSnapshot function from
useEffect, the unsubscribe function that
onSnapshot() returns will be called when the component un-mounts.
The query returns a
QuerySnapshot instance which contains the data from Firestore. We can Iterate over the documents and use it to populate state:
We use the snapshot
forEach method to iterate over each
DocumentSnapshot in the order they are stored on Cloud Firestore, and extract the documents unique identifier (
.id) and data (
.data()). We also store the
DocumentSnapshot in state to access it directly later.
Every time a document is created, deleted or modified on the collection, this method will trigger and update component state in realtime, neat!
We also check if
loading needs to be set back to
false. On the first load, this will disable loading - however after initial loading is complete we update the state in realtime so there is no need for the loading state again.
Rendering the todos #
Now we have the todos loading into state, we need to render them. A
ScrollView is not practical here as a list of TODOs with many items may cause performance issues when updating. Instead, we’ll use a
We'll want to render differently if
loading state is true:
When not loading, render the todos in a
FlatList using the
todos state we're populating:
You may notice that we’ve got a
Todo component rendering for each item. Let's create this as a
PureComponent; this means each row will only need re-render if one of its props (title or complete) changes.
Todo.js file in the root of your project:
This component renders out the title and whether the todo has been completed or not. Using
react-native-paper we return a
List.Item with an Icon on the left-hand side of the todo row/item. The icon changes based on the
complete status of the todo.
When the row is pressed, the
toggleComplete function is called; here we've set it to update the Firestore document with a reversed completion state.
Todos component is subscribed to the
todos collection, whenever an
update is made on an individual todo; the listener is called with our new data - which then filters down via state into our
Our final app should now look something like this:
At Invertase, we're proud to be contributing to open-source and the Firebase community, and we hope you'll love the new release of React Native Firebase.
Please get in touch via GitHub or Twitter if you have any issues or questions on this release.
With 💛 from the React Native Firebase team at Invertase.