Hooking Up Firebase to Your Redux Store

Josh Carlson
ITNEXT
Published in
7 min readMar 21, 2018

--

Click here to share this article on LinkedIn »

Firebase is an awesome real time database by the people at Google. It allows you to persist data without relying on any server side database and it updates in real time! In this tutorial I will show you how to use it with React-Redux while making a ToDo app. Yes I know, I am very original. Feel free to download the completed repo over at https://github.com/wickard/redux-firebase-tutorial.

To get started lets cheat and get some help from our friends at Facebook. Npm install create-react-app. Create-react-app is an awesome tool that will get you up in running with a react project fast. Go ahead and run create-react-app *your project name* in your terminal to create your new project folder. Cd into your project and run the command npm install. Npm start will run your project, take a minute to enjoy the spinning react logo.

Welcome back! Lets add a few more requirements before we get started. In your console type the following: npm install firebase redux react-redux redux-thunk. With our environment ready to go, it’s time to set up our firebase. Head over to https://firebase.google.com/ and create an account/project. Back in your project, lets create our firebase file in the src folder. I call mine firebase.js. At the top of the file import firebase like so:

import firebase from ‘firebase’

Back to the web! In your firebase home console click on the “add firebase to your web app” button. The result should look something like this.

<script src="https://www.gstatic.com/firebasejs/4.11.0/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "YOUR API KEY",
authDomain: "YOUR DOMAIN",
databaseURL: "YOUR DATABASE URL",
projectId: "etc",
storageBucket: "etc",
messagingSenderId: "etc"
};
firebase.initializeApp(config);
</script>

Lets get rid of those script tags we won’t need them. Copy over everything in between and lets get our database initialized. Your firebase.js file should look something like this:

import firebase from ‘firebase’
import uuid from 'uuid/v4'
const config = {authDomain: “YOUR DOMAIN”,databaseURL: “YOUR URL",projectId: “YOUR ID”,storageBucket: “YOUR BUCKET”,messagingSenderId: “YOUR ID”};firebase.initializeApp(config);const database = firebase.database()export default database

Now we just need a couple of functions that will add and remove tasks. Up above, we also imported the uuid library that is available with node. We will use it to give our tasks unique id’s.

export const addTaskToFirebase = (task) => { const id = uuid() database.ref(`/${id}`).set({ task, id })}export const removeTaskFromFirebase = (id) => {database.ref(`/${id}`).remove()}

To add something to our newly created firebase we will add a child to the root of our database at the unique id we create. To delete we simply call remove on the same route. Better documentation than I can provide lives over here: https://firebase.google.com/docs/web/setup.

All right! Our firebase is finally set up. Lets make a way to add tasks to our list. We can create a simple form to add items to the list. The submit button will push the tasks up to our firebase. Head over to the file App.js and add the following:

import {addTaskToFirebase, removeTaskFromFirebase}  from './firebase'export default class App extends Component {render() {return (
<div>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload. </p>
</div>
<div>
<form onSubmit={(e) => {
e.preventDefault()
addTaskToFirebase(e.target.task.value)}}>
<input type="text" name="task" />
<input type="submit" name="add task" />
</form>
</div>
<div>
<h2> Todo:</h2>
<ul>
we will map over our store later!
</ul>
</div>
</div>
);
}
}

Great!

Back in your firebase project page click on the database item in the left menu bar. On the top tab click on rules. Set the read and write rules to true so we can start adding tasks to our todo list. Don’t worry about the massive security issues we have right now, we can change the rules back later.

Now we run npm start from our tasks bar and we are able to sort of add tasks to our todo list. Nothing happens after we click add task on the page. But if we go back to the database tab in your firebase project, the tasks you add should be showing up over in your real time database.

This is great and all but we would probably like them to show up on the page. On to the store! In the tutorial I’m going to gloss over the details of implementing a redux store, if you would like more information check out https://egghead.io/courses/getting-started-with-redux. Make a new file in the src directory store.js. At the top we will handle our imports:

import { createStore, applyMiddleware } from 'redux'
import database from './firebase'
import thunkMiddleware from 'redux-thunk'

First we will make an action to fetch all of our tasks from firebase. The code for that will look something like this.

/**
* ACTION TYPES
*/
const GET_TASKS = 'get tasks'
/**
* ACTION CREATORS
*/
export const getTasks = (tasks) => ({type: GET_TASKS, tasks})
/**
* THUNKS
*/
export function getTasksThunk() {
return dispatch => {
const tasks = [];
database.ref(`/`).once('value', snap => {
snap.forEach(data => {
let task = data.val();
tasks.push(task)
})
})
.then(() => dispatch(getTasks(tasks)))
}
}

The action constant and action creators are pretty standard to any redux store so lets talk about the Thunk. Thunk is just a funny word that allows us to dispatch asynchronous functions to stores instead of only objects. This is available to us through thunk middleware. We use it here to make an async call our firebase, get all of the tasks inside of it, and then dispatch the lot to our redux store. This will happen through the reducer we create right after it here:

/**
* REDUCER
*/
function Reducer (state = [], action) {
switch (action.type) {
case GET_TASKS:
return action.tasks
default:
return state
}
}
export default createStore(Reducer, applyMiddleware(thunkMiddleware))

The last line creates our store with our reducer. We export it so we can use it with the rest of our application. Open up our main index file and add a few lines to it to import Provider from react-redux. We use provider to give all of our components access to our redux store(and our firebase through it).

...
import store from './store'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
...

Open up the App component again, and lets connect our store. Import the store and our getTaskThunk at the top of the file. We will be using to the connect function to well… connect our app to the store. More information on connect can be found in this great article over here https://medium.com/mofed/reduxs-mysterious-connect-function-526efe1122e4.

import { connect } from 'react-redux'
import { getTasksThunk } from './store'

In the jsx of the component add this line to display all of our tasks.

...
<div>
<h2> Todo:</h2>
<ul>
{this.props.tasks.map(item => <li key={item.id}>{item.task}<button
onClick={() => removeTaskFromFirebase(item.id)}>x</button></li>)} </ul>
</div>
...

Where are these tasks coming from? Glad you asked we get them from the store through the connect function it looks something like this:

const mapState = state => ({
tasks: state
})
const mapDispatch = dispatch => {
dispatch(getTasksThunk())
return {
}
}
export default connect(mapState, mapDispatch)(App);

All we have on our state is our tasks, so we can just map the task prop to our entire state. The mapDispatch function gives our component functions that have the ability to send information to the store. Because we want this one to execute on load, we place it outside of the returned object. Finally we connect the the component with the store on the last line and export it as the default. Your linter should yell at you at this point, make sure to delete the current export default in front of the class declaration near the top of the file.

Run another npm start and wallah! The tasks you’ve previously added to your firebase should appear. Try adding another one. Nothing >.>, but on refresh it shows up as expected. Lets fix that with some nifty event listeners provided to us by firebase.

Open up your store.js file and add the following:

/**
* ACTION TYPES
*/
...
const ADD_TASK = 'add task'
const REMOVE_TASK = 'remove task'
/**
* ACTION CREATORS
*/
...
export const addTask = (task) => ({type: ADD_TASK, task})
export const removeTask = (task) => ({type: REMOVE_TASK, task})
/**
* LISTENERS
*/
export function watchTaskAddedEvent(dispatch) {
database.ref(`/`).on('child_added', (snap) => { dispatch(addTask(snap.val()));
});
}
export function watchTaskRemovedEvent(dispatch) {
database.ref(`/`).on('child_removed', (snap) => {
dispatch(removeTask(snap.val()));
});
}

Firebase allows your application to listen for events and execute callbacks when the event fires. For a complete list of listeners check out the documentation here https://firebase.google.com/docs/database/admin/retrieve-data. We will be using the child_added and child_removed events. Whenever a item is added to our firebase it will dispatch the addTask function with the data the realtime database has just received. Likewise it will dispatch removeTask whenever an item is deleted.

Update the reducer to handle these new actions:

/**
* REDUCER
*/
function Reducer (state = [], action) {
switch (action.type) {
case GET_TASKS:
return action.tasks
case ADD_TASK:
return [...state, action.task]
case REMOVE_TASK:
return state.filter(task => task.id !== action.task.id)
default:
return state
}
}

All that left to hook up our new listeners to the app component. Open up App.js and add import the two new functions.

import { getTasksThunk, watchTaskAddedEvent, watchTaskRemovedEvent } from './store'

Add them to your mapDispatch and you are all set!

const mapDispatch = dispatch => {
dispatch(getTasksThunk())
watchTaskAddedEvent(dispatch)
watchTaskRemovedEvent(dispatch)
return {
}
}

Run npm start once more and bam! Our app adds and removes tasks on command. But you could do that before firebase. Open up another tab at the same address and marvel at how each page updates when you add or remove tasks!

Thanks for reading!

--

--

Josh lives in Orlando, FL. He enjoys fantasy football, watching twitch.tv, and ballroom dancing with his girlfriend Karen.