Connecting Firebase and Redux

The Firebase Realtime Database is great for building websites and Single-page applications(SPAs) without having to write any server code, but it has a few limitations:

  • The Javascript SDK takes a really long time to initialize. The process of loading the SDK(~90kb), initializing firebase, retrieving the logged in user and loading some data can take easily 10 to 20 seconds on a cold start, even on a good network connection.
  • The realtime database does not support offline caching or persistence on the web (it uses localStorage internally but doesn’t let you use it), so it’s really hard to make your app work offline, or improve the cold start performance by loading locally persisted data.
  • Using Firebase makes code hard to test and debug. Close-coupling of your UI code and application state with Firebase makes it difficult to reason about individual components in isolation.

To work around these limitations, it’s advisable to maintain the state of your applications separately, and sync it with Firebase only when required. TL;DR:

Redux is great for state management in front-end applications, and it addresses the concerns listed above fairly well:

  • The entire state of your application lives in a single plain Javascript object in the client (browser), and it can be initialized easily with default or persisted data.
  • Persisting the application state is straightforward, and so is loading it back.
  • Redux is great for testing as you’re dealing mostly with pure functions and plain javascript object, so it’s very easy to test them in isolation.

If you’re new to Redux, check out this video tutorial or read the docs. The rest of this post assumes familiarity with the basic Redux concepts like reducer, store, state, action, dispatch etc.

If this chart makes sense, you’re good to go!

Firebase → Redux

Here’s how we’ll connect Firebase and Redux:

  • Listen for changes in portions of the realtime database and dispatch actions to update the state in the Redux store whenever the data changes.
  • Listen for changes in portions the state managed by the Redux store, and make changes in the realtime database based using the updated state.

As an example, let’s listen for changes on the element at the path /message in the realtime database and let’s keep it in sync with state.message in the Redux store.

Here is the code for the Redux store:

Now, let’s define a function fromDb that accepts as arguments a Firebase database instance and the store’s dispatch function and dispatches an action to update the store whenever data changes in the database.

Now, we can call fromDb(firebase.database(), store.dispatch) anywhere in our code, and the Redux store will be updated whenever the data at /message in the realtime database is changed.

You can play with a working example of fromDb here (open the same link in two different tabs and try changing the data).

Redux → Firebase

In many cases, we would also like to modify the database whenever a change is made inside the store. For this, we can define a function fromStore which takes the updated state from the store and a firebase database instance and updates the database.

To ensure that fromStore is called whenever the state changes, we can add a change listener to the store by calling store.subscribe(() => fromStore(store.getState(), firebase.database())). Now, whenever the store state changes, the database is updated.

You can play with a working example of fromDb here (it’s the same as before, but now changes are written to the store instead of the database).

Firebase ⟷ Redux

More often than not, we would want to set up listeners on both sides together, so that the store is updated whenever the data in the database changes and vice versa. To do this, let’s define a helper function called linkStoreWithDb:

linkStoreWithDb takes two functions: fromDb and fromStore as arguments, and returns another function as its result. The resulting function can be called with the database instance and the Redux store as arguments, and it invokes fromDb and fromStore to set up the desired two-way binding. Here’s how we can use it in our code:

Firebase ⇎ Redux

Our setup for connecting the realtime database and the Redux store is looking good so far, but is has a couple of important shortcomings:

  • There is no way to turn off the two-way bindings. This could be a problem in complex Single-page applications where we might end up with dozens of dead listeners, or worse still, unexpected data changes.
  • Data changes in the realtime database trigger fromDb, which dispatches an action to update the store. This in turn invokes fromStore which tries to update the Firebase database with the same data. It would be best to avoid this wasteful write.

Let’s make some changes to fromDb and fromStore to address these issues:

The variable mustUpdate is used to track whether or not fromStore should write to the database. It is set to false immediately before dispatching the action inside fromDb and back to true immediately afterwards. This prevents the wasteful write.

fromDb now returns a function which can be called to remove the listener attached to the database. We’ll use it inside linkStoreWithDb, which is defined below:

Apart from setting up the listeners, linkStoreWithDb now also returns a a function unlink which can be called to remove the listeners and turn off the two-way binding.

If you’re using React, you can call linkStoreWithDb and unlink inside the component lifecycle methods to set up bindings for the data required by your component, and remove them when they are no longer needed:

Firebase ❤️ Redux

Our code for connecting the realtime database and the Redux store is quite robust now. However, this seems like a lot of work to set up a simple two way binding. While linkStoreWithDb is abstract and reusable, fromStore and fromDb contain some bits of reusable logic and some details specific to our example of binding '/message' in the database to state.message in the store.

Let’s extract the common logic into a new function:

Notice how we’ve replaced all the code specific to the message example with calls to path, actionCreator or selector. linkStoreWithPath additionally also keeps track of the previous state, and writes to the realtime database only if the incoming state is different. This prevents redundant writes.

We can now use it for to reimplement linkMessage as follows:

Isn’t that much better? Not only is it a lot less code, it’s a lot simpler, and there’s practically no logic involved except specifying the following:

  • path: The location of the data in the realtime database
  • actionCreator: A function that takes the value returned from the firebase snapshot and returns an action that can be dispatched to set the corresponding value in the store.
  • selector: A function takes the application state, and extracts the part that needs to be written.

So you want a library?

Now that we’ve understood how to connect Firebase and Redux, the natural next step is to ensure that we never have to actually implement it. firebase-redux is a nifty little library that provides the functions linkStoreWithDb and linkStoreWithPath so that you don’t have to write them. Check out the README for more details.

Thanks for reading! I hope you find this article and the library useful. In the next article (if there is one), we’ll go one step further and throw React into the mix and see how we can make React, Redux and Firebase all work together seamlessly. Have fun!

--

--

Founder, Jovian

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store