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:
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
localStorageinternally 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:
Don’t mix UI code and Firebase code.
Redux is great for state management in front-end applications, and it addresses the concerns listed above fairly well:
- Persisting the application state is straightforward, and so is loading it back.
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.
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
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).
JS Bin — fromDb
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).
JS Bin — fromStore
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 takes two functions:
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
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
fromStorewhich 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
fromStore to address these issues:
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
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,
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
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
linkStoreWithPath so that you don’t have to write them. Check out the README for more details.
firebase-redux - Connect your Redux store with the Firebase realtime database
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!