In this second part, we'll continue to develop the todo app but linking it to a firebase project.
I'm going to assume that you've read the previous tutorial and you have cloned the project from github and are ready to follow along.
The code we have so far is a functional application. You can run yarn start
(or yarn install
before that, if you haven't installed the packages) and see that we have a todo app where you can add items, mark them as complete, navigate between two routes and delete completed items.
Today we'll integrate our application with firebase. We'll end up using the database from firebase to manage the application state.
Then in the final part we'll deal with authentication.
Connecting to and using the database
Before we start coding we need to head over to firebase, create a project, and, just for now, change the database permission rules to true
for both read
and write
.
I have already written an Introduction to firebase's real-time database which guides you through how to setup all that. I write this tutorial series believing that you know the basics of firebase database and authentication services, if you don't I recommend you read my corresponding tutorials first.
Configuring the database into react
First we need to install firebase:
yarn add firebase
Then we need to initialize firebase as we've done in the database introduction tutorial.
To keep the project uncluttered, let's create a ./src/fire.js
file and add the following
import firebase from 'firebase';
const config = {
apiKey: "**********",
authDomain: "**********",
databaseURL: "**********",
projectId: "**********",
storageBucket: "**********",
messagingSenderId: "**********"
};
const fire = firebase.initializeApp(config)
export { fire }
There's nothing new there, we import firebase, copy and paste the configuration code that firebase gave us - when we create the project at Introduction to firebase's real-time database. Then we export it. The reason we aren't using export default
is because we'll export the different authentication methods from here later on.
Database CRUD
Head over at ./src/App.js
and let's start interacting with the database. We'll modify the existing code so that instead of creating, reading, updating and deleting items in local state, we'll perform those operations in the database.
To quickly recap, at the moment we have a state with few hard coded items. What we really want is for the state to be populated from the database. So let's do that.
// no constructor
state = {
items: {}
}
itemsRef = fire.database().ref('items')
We removed the hard-coded data from the state, then we instantiate an instance of the database reference (we basically tell firebase we need an items
object for this project).
Finally, using one of the "lifecycle methods" of our App
component (inherited by React.Component
) called componentWillMount
we can populate the state with the database data.
componentWillMount(){
this.itemsRef.on('value', data=> {
this.setState({
items: data.val()
})
})
}
From the documentation we learn that
componentWillMount() is invoked immediately before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering.
Hence it sounds as the correct "lifecycle method" for this purpose.
This article states that
[I]n ES6-style class components, the constructor plays the same role as componentWillMount, so if you already have a constructor, you can just put the code there.
Even though we do not have a constructor, it's still nice to know that we could have done something like this:
constructor(props) {
super(props);
this.state = {
items: {}
}
this.itemsRef = fire.database().ref('items')
this.itemsRef.on('value', data=> {
this.setState({
items: data.val()
})
})
}
Finally, we should also remove the database binding when component unmounts. "Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests"
componentWillUnmount(){
fire.removeBinding(this.itemsRef)
}
Creating items
In ./src/App.js
we already have the functionality to add items in the local state. We no longer need to do that, we just interact with the database directly and let the "real-time" feature of firebase update the state.
addItem=(e)=> {
e.preventDefault();
this.itemsRef.push({
item: this.todoItem.value,
completed: false
})
}
Again, we've already covered this functionality. Remember, unlike when setting local state, firebase auto-generates the unique identifier for us. So over at firebase, the above code would generate something like this:
{
"items": {
"-L1Ds8FqPAPsFiXgUhQ2" : {
"completed" : false,
"item" : "Get Milk"
}
}
}
Modifying items
Next, we already have the functionality to change completed
from false
to true
.
completeItem=(id)=>{
this.itemsRef.update({
[id]:{
...this.state.items[id],
completed: true
}
})
}
Similar to what we've already done, but this time around, we're updating the database content.
Deleting items
Using the same code as when we explored firebase databases let's delete a completed item.
deleteItem = (id) => {
this.itemsRef.update({
[id]: null
})
}
Conclusion
That's it. The application is not ready for production, as we are basically allowing anyone to add data to our database. In the final part of this mini series we'll use authentication to restrict who can read and write the data.
The project as it stands so far can be found at the same github repository. As you'll see, I'm using different branches corresponding to each part of this series. Stay tuned for the third and final part
Top comments (0)