Reactjs CRUD Firestore example – Firebase Tutorial

Reactjs crud firestore example

Tutorial: “Reactjs CRUD Firestore example – Firebase”

Firestore is a NoSQL document database built for automatic scaling, high performance, and ease of application development. It supports offline mode so our app will work fine (write, read, listen to, and query data) whether device has internet connection or not, it automatically fetches changes from our database to Firebase Server. So In the tutorial, I introduce an example “Reactjs CRUD Firestore example”.

– I draw a fullstack overview diagram architecture from Reactjs frontend to Firestore.
– I illustrate details about react-firestore CRUD operations.
– I implement Reactjs application to do CRUD request (Post/Get/Put/Delete) to Firebase Firestore.

Related posts:


Overvier Architecture Diagram – Reactjs CRUD Firestore

Reactjs Crud firetore overall diagram architecture
Reactjs Crud firetore overall diagram architecture

Reactjs CRUD Application is designed with 2 main layers:

– React.js components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
– Firestore Service is used by above React Components to fetch (Post/Put/Get/Delete) data to Firestore.

Reactjs CRUD Application defines 5 components:

  • Home.js is used serve as the landing page for your app.
  • AppNavbar.js is used to establish a common UI feature between components.
  • CustomerList.js is used to show all customers in the web-page
  • CustomerEdit.js is used to modify the existed customer
  • App.js uses React Router to navigate between components.

Integrative Project Goal between Reactjs CRUD Firestore

Reactjs Home page:

Project Goal Home Page
Project Goal Home Page

Reactjs add data:

Project Goal Reactjs Add a Customer to Firestore
Project Goal Reactjs Add a Customer to Firestore
React Firebase CRUD Example - Adding customers
React Firebase CRUD Example – Adding customers

Reactjs List all data:

Reactjs application show list data in Firestore
Reactjs application show list data in Firestore

Reactjs update data:

Project Goal Reactjs Update Customer to Firestore
Project Goal Reactjs Update Customer to Firestore
React Firebase Update customer
React Firebase Update customer

Reactjs delete a customer with id=2, check the Customer List after deleting:

Reactjs show list customers after doing CRUD operations
Reactjs show list customers after doing CRUD operations

Check Firestore after do CRUD operations:

React Firestore delete a customer
React Firestore delete a customer

Firestore CRUD Operation

We use instance of firebase.firestore.CollectionReference to read/write data from the Firestore.

const customerRef = firebase.firestore().collection('/customers');

Firestore Read operation

– Read list once using get():

customerRef.get().then((snapshot) => {
  vat customers = [];

  snapshot.forEach((childSnapshot) => {
    var id = childSnapshot.id;
    var data = childSnapshot.val();
    // ...

    tutorials.push({ id: id, firstname: data.firstnam, lastname: data.lastname, ...});
  });
});

– Read List with listening to the data changes using onSnapshot():

customersRef.onSnapshot((snapshot) => {
  snapshot.docChanges().forEach((change) => {
    if (change.type === "added") {
      console.log("Customer is added: ", change.doc.data());
    } else if (change.type === "modified") {
      console.log("Modified customer: ", change.doc.data());
    } else if (change.type === "removed") {
      console.log("Removed Customer: ", change.doc.data());
    }
  });
});

– Listening for all value events on a List reference

customersRef.onSnapshot( (snapshot) => {
  snapshot.forEach((childSnapshot) => {
    let id = childSnapshot.id;
    let childData = childSnapshot.val();
    // ...
  });
});

– Remove the listener:

let unsubscribe = customersRef.onSnapshot((snapshot) => {
  // ...
});

// Stop listening to changes
unsubscribe();

Firestore Create operation

– Create a new document in collection using add():

customersRef.add({
  firstname: "Jack",
  lastname: "Smith",
  ...
}).then((docRef) => {
    console.log("Customer is created with ID: ", docRef.id);
}).catch((error) => {
    console.error("Error: ", error);
});

Firebase Realtime Database Update operation

– Update document in collection by id:

* destructive update using set(): delete everything currently in place, then save the new value

customersRef.doc(id).set({
  firstname: 'Jack',
  lastname: 'Smith',
  ...
});

* non-destructive update using update(): only updates the specified values

customersRef.doc(id).update({
  firstname: 'Jack'
});

Firestore Delete operation

– Delete an document in collection by id:

customersRef.doc(id).delete();

– Delete entire List: not recommended. Check at here.

Set up the Firestore Project

To setup Firestore project, you can follow the guide at here.

Firebase configuration screenshot
Firebase configuration screenshot

Setup Reactjs Application with Firebase

Create React App is a command line utility that generates React projects for you. It’s a convenient tool because it also offers commands that will build and optimize your project for production.
The create-react-app will set up everything you need to run a React application.

– Create a new project in the app directory with Yarn.

yarn create react-app app

After the app creation process completes, navigate into the app directory and install Bootstrap, cookie support for React, React Router, and Reactstrap.

Reactstrap: This library contains React Bootstrap 4 components that favor composition and control. The library does not depend on jQuery or Bootstrap javascript.
– React Router: Components are the heart of React’s powerful, declarative programming model. React Router is a collection of navigational components that compose declaratively with your application.

cd app
yarn add bootstrap@4.1.3 react-cookie@3.0.4 react-router-dom@4.3.1 reactstrap@6.5.0

Import, we install firebase to reactjs project by cmd:

$npm i firebase

Add Firestore configuration to Reactjs environments variable

– Create a file ./src/util/firebase.js to add firebase configuration as below content:

import firebase from "firebase";

let firebaseConfig = {
    apiKey: "AIzaSyBVm7nQJqpJxXMr63spZydivMPgoECs_R0",
    authDomain: "loizenjava-reactjs-crud-db.firebaseapp.com",
    projectId: "loizenjava-reactjs-crud-db",
    storageBucket: "loizenjava-reactjs-crud-db.appspot.com",
    messagingSenderId: "610864533391",
    appId: "1:610864533391:web:c3adddd2aa1ac818fe45fa",
    measurementId: "G-LD2JGWF657"
  };

// Initialize Firebase
firebase.initializeApp(firebaseConfig);
export default firebase.database();

Implement Reactjs CRUD Firestore Service: Post/Get/Put/Delete operations

Reactjs Project structure:

Reactjs CRUD Firestore Project Structure
Reactjs CRUD Firestore Project Structure

Reactjs Architecture Diagram:

Reactjs Firestore overall architecture diagram
Reactjs Firestore overall architecture diagram

For more details, we go back to the session: Overvier Architecture Diagram – Reactjs CRUD Firestore

We implement a FirestoreService in file ./src/services/FirestoreService.js as below:

import firebase from '../util/firebase';

const db = firebase.ref('/customers');
let customers = [];

class FirebaseService {
  
  addCustomer = (customer) => {
    db.push(customer);
  };

  getAll() {
    return db;
  }

  get(key) {
    return db.child(key);
  }

  update(key, value) {
    return db.child(key).update(value);
  }

  delete(key) {
    return db.child(key).remove();
  }
}

export default new FirebaseService();

Build React Crud Firestore Navigation Bar Component

import React, { Component } from 'react';
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
import { Link } from 'react-router-dom';

export default class AppNavbar extends Component {
  constructor(props) {
    super(props);
    this.state = {isOpen: false};
    this.toggle = this.toggle.bind(this);
  }

  toggle() {
    this.setState({
      isOpen: !this.state.isOpen
    });
  }

  render() {
    return <Navbar color="dark" dark expand="md">
      <NavbarBrand tag={Link} to="/">Home</NavbarBrand>
      <NavbarToggler onClick={this.toggle}/>
      <Collapse isOpen={this.state.isOpen} navbar>
        <Nav className="ml-auto" navbar>
          <NavItem>
            <NavLink
              href="https://loizenjava.com">loizenjava.com</NavLink>
          </NavItem>
          <NavItem>
            <NavLink href="https://github.com/loizenjava">GitHub</NavLink>
          </NavItem>
        </Nav>
      </Collapse>
    </Navbar>;
  }
}

Create Reactjs React Crud Firestore Home Page Component

Project Goal Home Page
Project Goal Home Page
import React, { Component } from 'react';
import './App.css';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
import { Button, Container } from 'reactstrap';

class Home extends Component {
  render() {
    return (
      <div>
        <AppNavbar/>
        <Container fluid>
          <Button color="link"><Link to="/customers">Manage Customer List</Link></Button>
        </Container>
      </div>
    );
  }
}

export default Home;

Build React Crud Firestore CustomerList Component

Reactjs application show list data
Reactjs application show list data

– CustomerList Component will fetch a list of customers from Firebase Realtime database and then shows all of them on a Bootstrap table.
– The CustomerList has 3 buttons:

  • Add Customer & Edit are used to link to a url /customers/new that will map with CustomerEdit component
  • Delete button is used to remove a Customer entity from Firebase Realtime Database based on a given key through an async function remove(key) that will do a request with DELETE method to Firebase.

Detail coding:


import React, { Component } from 'react';
import { Button, ButtonGroup, Container, Table } from 'reactstrap';
import AppNavbar from './AppNavbar';
import { Link } from 'react-router-dom';
import FirebaseService from '../services/FirebaseService';

class CustomerList extends Component {

  constructor(props) {
    super(props);
    this.state = {customers: [], isLoading: true};
    this.remove = this.remove.bind(this);
  }

  componentDidMount = () => {
    FirebaseService.getAll().on("value", this.onDataChange);
  }

  componentWillUnmount = () => {
    FirebaseService.getAll().off("value", this.onDataChange);
  }

  onDataChange = (items) => {
    console.log(items);
    let customers = [];
    items.forEach(item => {
      let data = item.val();
      customers.push({
        key: item.key,
        firstname: data.firstname,
        lastname: data.lastname,
        address: data.address,
        age: data.age,
        copyrightby: "https://loizenjava.com"
      });
    });

    this.setState({
      customers: customers,
      isLoading: false
    });
  }

  async remove(key) {
    FirebaseService.delete(key)
    .then(() => {
      let updatedCustomers = [...this.state.customers].filter(i => i.key !== key);
      this.setState({customers: updatedCustomers});
    });
  }

  render() {
    const {customers, isLoading} = this.state;

    if (isLoading) {
      return <p>Loading...</p>;
    }

    const customerList = customers.map(customer => {
      return <tr key={customer.key}>
        <td style={{whiteSpace: 'nowrap'}}>{customer.firstname}</td>
        <td>{customer.lastname}</td>
        <td>{customer.age}</td>
        <td>{customer.address}</td>
        <td><a href={customer.copyrightby}>{customer.copyrightby}</a></td>
        <td>
          <ButtonGroup>
            <Button size="sm" color="primary" tag={Link} to={"/customers/" + customer.key}>Edit</Button>
            <Button size="sm" color="danger" onClick={() => this.remove(customer.key)}>Delete</Button>
          </ButtonGroup>
        </td>
      </tr>
    });

    return (
      <div>
        <AppNavbar/>
        <Container fluid>
          <div className="float-right">
            <Button color="success" tag={Link} to="/customers/new">Add Customer</Button>
          </div>
          <h3>Customer List</h3>
          <Table className="mt-4">
            <thead>
              <tr>
                <th width="20%">Firstname</th>
                <th width="20%">Lastname</th>
                <th width="10%">Age</th>
                <th>Address</th>
                <th>Copyrightby</th>
                <th width="10%">Actions</th>
              </tr>
            </thead>
            <tbody>
            {customerList}
            </tbody>
          </Table>
        </Container>
      </div>
    );
  }
}

export default CustomerList;

Build React Crud Firestore CustomerEdit Component

Project Goal Reactjs Update Customer to Firestore
Project Goal Reactjs Update Customer to Firestore

import React, { Component } from 'react';
import { Link, withRouter } from 'react-router-dom';
import { Button, Container, Form, FormGroup, Input, Label } from 'reactstrap';
import AppNavbar from './AppNavbar';
import FirebaseService from '../services/FirebaseService';

class CustomerEdit extends Component {

  emptyCustomer = {
    key: '',
    firstname: '',
    lastname: '',
    age: "",
    address: '',
    copyrigtby: 'https://loizenjava.com'
  };

  constructor(props) {
    super(props);
    this.state = {
      item: this.emptyCustomer
    };
  }

  componentDidMount = () => {
    let key = this.props.match.params.key
    if (key !== 'new') {
      FirebaseService.get(key).on("value", this.onDataChange);
    }
  }

  componentWillUnmount = () => {
    FirebaseService.getAll().off("value", this.onDataChange);
  }

  onDataChange = (item) => {
    let data = item.val();
    let customer = {
      key: item.key,
      firstname: data.firstname,
      lastname: data.lastname,
      age: data.age,
      address: data.address,
      copyrightby: 'https://loizenjava.com'
    };

    this.setState({
      item: customer,
    });
  }

  handleChange = (e) => {
    const target = e.target;
    const value = target.value;
    const name = target.name;
    let item = {...this.state.item};
    item[name] = value;
    this.setState({item});
  };

  handleSubmit = (e) => {
    e.preventDefault();
    const {item} = this.state;
    let key = this.props.match.params.key
    if (key !== 'new') {
      FirebaseService.update(key, item);
    } else {
      FirebaseService.addCustomer(item);
    }

    this.props.history.push('/customers');
  };

  render = () => {
    const {item} = this.state;
    const title = <h2>{item.key ? 'Edit Customer' : 'Add Customer'}</h2>;

    return <div>
      <AppNavbar/>
      <Container>
        {title}
        <Form onSubmit={this.handleSubmit}>
          <FormGroup>
            <Label for="firstname">Firstname</Label>
            <Input type="text" name="firstname" id="firstname" value={item.firstname || ''}
                   onChange={this.handleChange} autoComplete="firstname"/>
          </FormGroup>
          <FormGroup>
            <Label for="lastname">Lastname</Label>
            <Input type="text" name="lastname" id="lastname" value={item.lastname || ''}
                   onChange={this.handleChange} autoComplete="lastname"/>
          </FormGroup>          
          <FormGroup>
            <Label for="age">Age</Label>
            <Input type="text" name="age" id="age" value={item.age || ''}
                   onChange={this.handleChange} autoComplete="age"/>
          </FormGroup>
          <FormGroup>
            <Label for="address">Address</Label>
            <Input type="text" name="address" id="address" value={item.address || ''}
                   onChange={this.handleChange} autoComplete="address"/>
          </FormGroup>
          <FormGroup>
            <Button color="primary" type="submit">Save</Button>{' '}
            <Button color="secondary" tag={Link} to="/customers">Cancel</Button>
          </FormGroup>
        </Form>
      </Container>
    </div>
  }
}

export default withRouter(CustomerEdit);

Edit Reactjs App.js Component

App.js uses React Router to navigate between components.

  • path “/” is mapped with Home component
  • path “/customers” is mapped with CustomerList component
  • path “customers/:id” is mapped with CustomerEdit component
import React, { Component } from 'react';
import './App.css';
import Home from './Home';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import CustomerList from './CustomerList';
import CustomerEdit from './CustomerEdit';

class App extends Component {
  render() {
    return (
      <Router>
        <Switch>
          <Route path='/' exact={true} component={Home}/>
          <Route path='/customers' exact={true} component={CustomerList}/>
          <Route path='/customers/:key' component={CustomerEdit}/>
        </Switch>
      </Router>
    )
  }
}

export default App;

Sourcecode

reactjs-firestore-crud-example

– Github:

Reactjs CRUD Firestore – Github

Leave a Reply

Your email address will not be published. Required fields are marked *