In this React 16+ tutorial, we are going to integrate Firebase Database service and Material UI library to create a working TODO application having CRUD (Create Read Update and Delete) operations in React Js application. Users can Add, List, Update or Delete todos from a list by communicating with Firebase.
Firebase is very popular for many cloud-based services like Realtime database, Authentication services, Testing, Storage, Crash analysis, Hosting, and many more.
The combination of React and Firebase can move applications to the next level. In this tutorial, we are going to integrate Firebase NoSQL Database service using which a user can do CRUD operations realtime in the application.
To style our React TODO application, we’ll be using Material UI. It is a very popular React specific UI library to add beautifully designed components like Inputs, Buttons, Modal, Grids, Lists, Icons, etc very quickly.
So, with these power packs, let’s get started!
Create a React Application
First, we’ll create a new React application using npx create-react-app
command
$ npx create-react-app react-firebase-materialui-todo-app
Move inside the react app
$ cd react-firebase-materialui-todo-app
Run application
$ npm start
SignIn Firebase and Get App Credential
To connect our React application with a Firebase application, you need to follow these steps:
# Step 1: SignIn or SignUp Firebase
Register or Login Firebase with your credentials.
# Step 2: Create a new or use existing Firebase application
After login, click on the Create a project button.
Then enter project name, then click Continue
Click Continue again
Finally hit Create project
Bingo!!!
# Step 4: Enable Database and Hosting Services
Now on the left sidebar click on Develop then enable Database and Hosting one by one
Make sure you create a Database in the Test Mode during development.
# Step 3: Click on Web icon to create a new application
We are ready with Firebase project, next, we need to create a new Firebase application. For that, go to Project Overview then hit Web icons
Enter the Firebase application name then enable Hosting( Optional )
# Step 4: Go to Firebase and Check Credential
Click on application’s configuration icon
Under the General tab, scroll down to see Config object
Keep this object handy which we’ll use later in this tutorial.
Install Firebase package in React
To connect and control Firebase from React application’s CLI, we’ll install the official firebase
package.
$ npm install --save firebase
Connect React with Firebase Application
To connect our Firebase application with React application, we need to create a firebase config file inside the React project folder.
Create a new file firebase-config.js inside the src at the root folder. Update this file with the following code.
// src/firebase-config.js
import firebase from 'firebase';
const firebaseApp = firebase.initializeApp({
apiKey: "AIzaSyBMgYcu4ifbQd5o29cfYbLuZ4SWYfdYHuI",
authDomain: "freakyjolly-c91ee.firebaseapp.com",
databaseURL: "https://freakyjolly-c91ee.firebaseio.com",
projectId: "freakyjolly-c91ee",
storageBucket: "freakyjolly-c91ee.appspot.com",
messagingSenderId: "997516632089",
appId: "1:997516632089:web:46746c6f9815f43e2c4a88",
measurementId: "G-V6X4RQ389S"
});
const db = firebaseApp.firestore();
export default db;
We have to import the firebase
module using which we initialized the React application to connect with Firebase application credential object.
Install Material UI package
We are going to style out the React ToDo application using the Material UI component. Run the following npm command to install the Material UI core
and Icons
package.
$ npm install @material-ui/core @material-ui/icons
Finally Start Building the TODO Application
We’ll be using various Material UI components like List, Dialog, Icons, Button, Input etc to create nice looking layout.
To keep our application simple, we’ll have everything in the App.js functional component using Hooks and methods. But you can have separate components for Todo input, List and Dialog modal to update.
For performing CRUD operations, the Firebase library methods will be used to Add, List, Update and Delete Todo’s.
# Add Input and Button to Create New TODO
Import the firebase-config we created in previous steps and firebase
import db from './firebase-config'
import firebase from 'firebase';
Also, import the icons from the Material UI icons package. Currently, we have Add icon. The components also need to be imported as well
import { AddCircleOutlineRounded } from '@material-ui/icons';
import { Button, TextField, Container } from '@material-ui/core';
For adding and maintain the Todo object, add two useState
hook methods.
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
To get a real-time snapshot of our todos list from Firestore collection, we’ll add the useEffect
hook.
useEffect(() => {
console.log('useEffect Hook!!!');
db.collection('todos').orderBy('datetime', 'desc').onSnapshot(snapshot => {
console.log('Firebase Snap!');
setTodos(snapshot.docs.map(doc => {
return {
id: doc.id,
name: doc.data().todo,
datatime: doc.data().datatime
}
}))
})
}, []);
The setTodos()
hook method is updating todos
array with updated snapshot using map()
method.
On the Button, we will have an onClick
event handler to call addTodo
function
const addTodo = (event) => {
event.preventDefault();
db.collection('todos').add({
todo: input,
datetime: firebase.firestore.FieldValue.serverTimestamp()
})
setInput('');
}
We have also used the firestore’s FieldValue to assign datetime
with server time.
To create a form using Material UI, add the following template inside the App function’s return
return (
<Container maxWidth="sm">
<form noValidate>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="todo"
label="Enter ToDo"
name="todo"
autoFocus
value={input}
onChange={event => setInput(event.target.value)}
/>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
onClick={addTodo}
disabled={!input}
startIcon={<AddCircleOutlineRounded />}
>
Add Todo
</Button>
</form>
</Container >
);
This will create a nice layout with Button disabled when no input is typed by user.
# List & Delete Todos
To create the list with delete and update icon, we’ll use the Material UI component.
import { AddCircleOutlineRounded, DeleteOutlineRounded, Edit } from '@material-ui/icons';
import { Button, TextField, Container, IconButton, List, ListItem, ListItemSecondaryAction, ListItemText, Dialog, DialogContent, DialogActions } from '@material-ui/core';
Add the deleteTodo
function to remove todo from Firestore using the delete() method.
const deleteTodo = (id) => {
db.collection('todos').doc(id).delete().then(res => {
console.log('Deleted!', res);
});
}
Also, add List component in the return() after form
<List dense={true}>
{
todos.map(todo => (
<ListItem key={todo.id} >
<ListItemText
primary={todo.name}
secondary={todo.datetime}
/>
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="Edit" onClick={() => openUpdateDialog(todo)}>
<Edit />
</IconButton>
<IconButton edge="end" aria-label="delete" onClick={() => deleteTodo(todo.id)}>
<DeleteOutlineRounded />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
))
}
</List>
# Update Todo using Dialog Modal
As we are doing all stuff in the App itself, so we need two more hooks for updating the todo
const [open, setOpen] = useState(false);
const [update, setUpdate] = useState('');
const [toUpdateId, setToUpdateId] = useState('');
First will control the Dialog open and close state. The second will be used for the Input control inside the Dialog. The third hook is getting used to keeping the id of todo which we are going to update.
Update a list todo item using the Dialog modal box. Add the Dialog Material UI component
<Dialog open={open} onClose={handleClose}>
<DialogContent>
<TextField
autoFocus
margin="normal"
label="Update Todo"
type="text"
fullWidth
name="updateTodo"
value={update}
onChange={event => setUpdate(event.target.value)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={editTodo} color="primary">
Save
</Button>
</DialogActions>
</Dialog>
Finally, our App.js file will have the following code
import React, { useState, useEffect } from 'react';
import './App.css';
import db from './firebase-config'
import firebase from 'firebase';
import { AddCircleOutlineRounded, DeleteOutlineRounded, Edit } from '@material-ui/icons';
import { Button, TextField, Container, IconButton, List, ListItem, ListItemSecondaryAction, ListItemText, Dialog, DialogContent, DialogActions } from '@material-ui/core';
function App() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const [open, setOpen] = useState(false);
const [update, setUpdate] = useState('');
const [toUpdateId, setToUpdateId] = useState('');
useEffect(() => {
console.log('useEffect Hook!!!');
db.collection('todos').orderBy('datetime', 'desc').onSnapshot(snapshot => {
console.log('Firebase Snap!');
setTodos(snapshot.docs.map(doc => {
return {
id: doc.id,
name: doc.data().todo,
datatime: doc.data().datatime
}
}))
})
}, []);
const addTodo = (event) => {
event.preventDefault();
db.collection('todos').add({
todo: input,
datetime: firebase.firestore.FieldValue.serverTimestamp()
})
setInput('');
}
const deleteTodo = (id) => {
db.collection('todos').doc(id).delete().then(res => {
console.log('Deleted!', res);
});
}
const openUpdateDialog = (todo) => {
setOpen(true);
setToUpdateId(todo.id);
setUpdate(todo.name);
}
const editTodo = () => {
db.collection('todos').doc(toUpdateId).update({
todo: update
});
setOpen(false);
}
const handleClose = () => {
setOpen(false);
};
return (
<Container maxWidth="sm">
<form noValidate>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="todo"
label="Enter ToDo"
name="todo"
autoFocus
value={input}
onChange={event => setInput(event.target.value)}
/>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
onClick={addTodo}
disabled={!input}
startIcon={<AddCircleOutlineRounded />}
>
Add Todo
</Button>
</form>
<List dense={true}>
{
todos.map(todo => (
<ListItem key={todo.id} >
<ListItemText
primary={todo.name}
secondary={todo.datetime}
/>
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="Edit" onClick={() => openUpdateDialog(todo)}>
<Edit />
</IconButton>
<IconButton edge="end" aria-label="delete" onClick={() => deleteTodo(todo.id)}>
<DeleteOutlineRounded />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
))
}
</List>
<Dialog open={open} onClose={handleClose}>
<DialogContent>
<TextField
autoFocus
margin="normal"
label="Update Todo"
type="text"
fullWidth
name="updateTodo"
value={update}
onChange={event => setUpdate(event.target.value)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={editTodo} color="primary">
Save
</Button>
</DialogActions>
</Dialog>
</Container >
);
}
export default App;
That’s it now you can run the application by hitting $ npm start
Source Code
Find source code in the GitHub repository here.
Conclusion
In the above tutorial, we got to learn how to easily integrate Firebase services in the React application. We used Firestore’s real-time NoSQL database to return todos collection inside the useEffect
hook to create a list.
For deleting and updating the todo we used material icons and called functions to perform CRUD operations.
Let us know if it helped… do share your feedback
Stay Safe!
very useful thanks