Building a SAAS App with React, Redux and Firebase - Part 4

We're continuing our series on building a simple Software-As-A-Service (SAAS) application using React, Redux and Firebase as a Backend-As-A-Service.

In the last section we adding the remaining Authorization features using Firebase Auth, protected our private routes with a Higher Order Component and did some wrangling to get our application running smoothly.

In this section we're going to add CRUD features to our posts resource and connect to Firestore which is the latest database system offered by Firebase.

NOTE: There are many places where we can improve on the demo application, but the aim of this post is to simply get our feet wet with Firebase.  

Feel free to Submit a PR on the source repo

The Plan

  • Configure Firestore in Firebase Console
  • Application Configuration for Firestore
  • Add Posts Index
  • Refactor Posts Index Component
  • Add Posts New Modal Feature
  • Add Posts Edit Modal Feature
  • Add Posts Delete Feature

Configure Firestore in Firebase Console

Go to https://console.firebase.google.com and login to your console.  Navigate to Develop > Database section:

Create a Firestore DB, make set it to test to allow public access (this is just for development).

Next, add a "posts" collection:

And create an example document:

  • title (string) - example title
  • body (string) - example body

This gives us the backend DB to connect to in our app and a single example post to pull into our application.

Application Configuration for Firestore

Next let's configure our application to connect to firestore DB.

Create a src/utils/firebase/firestore.js file with the following content:

This will give us a reusable module to quickly connect to firestore from anywhere in our application.

Add Posts Index

We're going to build our posts index feature first and fetch the data from Firestore.

Open src/components/Posts/index.js and update it's content to the following:

A few notes about this component:

  • It uses Semantic-UI-React's components for layout.
  • It connects to the redux store and uses the posts slice of state.
  • It attaches all postsActions to the props.
  • It dispatches fetchPosts on componentWillMount.
  • It displays a loading placeholder provided by Semantic-UI-React while the async call to Firebase is being made.

Add posts Redux Files

Let's set up the action creators and reducer for posts.

Create a src/actions/posts.js file with the following content:

The fetchPosts function is a thunk that returns the promise from the async call to Firebase.  After the response is complete we dispatch setPosts to update the redux store.

Next, create a src/reducers/posts.js file with the following content:

Add SET_POSTS to our types.  

Open src/actions/types.js and add:

export const SET_POSTS = 'SET_POSTS'

Finally, we need to update our root reducer to include the new posts reducer.

Open src/reducers/index.js and update it with the following content:

Test it in the browser

Fire up the server with yarn start and visit: http://localhost:3000.  After logging in, and navigating to the /posts page, you should see brief placeholder while the posts are fetched from firebase:

And once they're loaded, they should be displayed in table format:

Great! We're effectively pulling data in from Firebase, and displaying it in our application.

Refactor Posts Index Component

As you can see our Posts component is getting very large and difficult to reason about.  Let's do some refactoring to help improve the component's readability and maintainability.

Open src/components/Posts/index.js and update it's contents to the following:

You'll notice some important changes:

  • We moved the Placeholder to a separate component which we can expand on for other sections of the app.
  • We moved the PostRow content to it's own component
  • We're using the new component to simplify the render method, specifically inside the <Table.Body>.

Let's create these new components.

Create a src/components/common/placeholders.js file with the following content:

We can extend this and customize it as we need whenever we need a placeholder.

Create src/components/Posts/PostRow.jsx with the following content:

Refactoring is an important part of the development process, and we wanted to include it here to help readers get a feel for refactoring opportunities.

Add Posts New Modal Feature

With the refactor out of the way and our code base in a more manageable state, let's add the next feature.  

We're going to handle our create/update events using the Modal component provided by Semantic-UI-React, and our global state will know exactly when to show the Modal, and pass it the relevant data.

Let's first set this up in Posts index.

Open src/components/Posts/index.js and update it to have the following content:

Some important changes:

  • We're importing and rendering a new PostFormModal component.
  • We've adjusted our redux state to pull in everything from the posts slice.  This will make more sense as we progress through the features.

Let's create PostFormModal.

Create a src/components/Posts/PostFormModal.jsx file with the following content:

Some details about the component:

  • Using Semantic-UI-React's component for presentation.
    Connected to redux  using showModal and currentPost state data to control behavior.
  • Using Formik to handle internal form state, validation and submission.
  • Using Yup schema for validation on the form.
  • Connected directly to the firestore to submit data to Firebase.
  • Using new action creator: addPost after a successful response.

Take moment to carefully read over the file, and understand how it works.  We're going to extend this modal for the Edit Feature in the next section, but for now let's continue with our Redux set up.

Open src/actions/posts.js and add these new action creators:

Open src/actions/types.js and add the new types:

export const TOGGLE_POST_FORM = 'TOGGLE_POST_FORM'
export const ADD_POST = 'ADD_POST'

Open src/reducers/posts.js and update the reducer to the following:

This wraps up the redux connection for our Post Creating Feature.  Let's test it out in the browser:

Test it out in the browser:

Start the server with yarn start and navigate to the /posts page.

Now if you click on the Add Post button, you should see a modal open where you can enter the title and body, then once submitted it will store a new document in Firestore, and update the Posts list.

Edit Post Feature

We need to make some changes to the PostRow component to enable the Edit button.

Open src/components/Posts/PostsRow.jsx and update it with the following content:

Next up, let's reuse the PostFormModal to edit an existing post as well.  We're going to make some significant changes to the component, so here's the fully refactored component.

Open src/components/Posts/PostFormModal.jsx and update it's content to the following:

This splits some of the logic up to handle the case in which we're editing an existing post.  It re-uses the validations and has some special considerations for when it's an existing Post such as whether or not a postId is present.  

Again, I know it's a lot of code, but take your time to read through it.  We needed to do some conditional checking to set the value of the form fields, but everything else should be pretty straight forward.

We also added another action creator: updatePost.  So let's tackle that redux flow next.

Open src/actions/posts.js and add the following action creator:

export const updatePost = (post) => ({
  type: types.UPDATE_POST, post
})

Open src/actions/types.js and add the constant:

export const UPDATE_POST = 'UPDATE_POST'

Open src/reducers/posts.js and add another case:

There is room for improvement on how our redux data is structured that would eliminate the need for these complex updates in the reducer.

Test it out in the browser:

Start the server with yarn start and navigate to the /posts page.

Now if you click on the Edit button, you should see a modal open prefilled with the Post data.  You should be able to edit the content and update the post.

Delete Post Feature

Ok, so after all this craziness, we're ready to add our final feature.  The delete feature will need only a single file change.

Open src/components/Posts/PostRow.jsx and update it's content to this:

We added a handleDelete method and are updating the posts.list state slice on success.

Wrap up

Well that wraps up all the features we wanted to add to our simple Software-As-A-Service App using Firebase.

There are many, many improvements we can make to the codebase, and many other features that the Firebase ecosystem offers.  

This guide was intended to be a primer on getting started with using Firebase as a backend with React/Redux on the frontend.

I hope this was helpful and as always if you have ideas on how to improve the sample application, or any feedback follow me on twitter: @tmartin8080 or post them in the comments.

UPDATE: We are planning on adding a deployment to Firebase section in another post.  Stay tuned!

Happy Coding!

Series:

Source code

https://github.com/devato/react-redux-firebase