React Native, Redux and Firebase for experts, by dummies

Erez Rokah
Rumors
5 min readJun 6, 2017

--

Our main vision at Rumors is to provide people a way to generate value for each other.

We are strong believers in paying it forward, thus comes this publication.

The first step of executing our vision of becoming the digital lemonade stand was to develop a small app to test some of our hypotheses about the marketplace we’re planning to create.

We’ve chosen React Native + Firebase as our technological stack, since we believed (and still believe) it will allow us to move the fastest.

Since then we’ve released several versions of the app and would like to share our experiences and mostly the issues we’ve encountered while working with React Native, Firebase and specifically Redux.

  • If you don’t feel like reading you can go straight to the code on Github.

If you’re not familiar with Redux, going through their documentation is probably the first thing you should do before reading further.

Redux helps you manage the state of your application in a way that really simplifies things.

We feel that without it our app would have become overly complicated at a very early stage, and by using it we’ll be able to scale our code easily.

Redux works very well when changes to your application are invoked from within your application, but can get a bit hard to figure out on how and where to handle changes that are external to your application (for example responding to external events).

Utilizing Firebase (or any other event driven data service) on the other hand relies on external events, where in order to retrieve data (and updates to data) you listen to events.

For example, lets say you have a ‘messages’ collection in your Firebase database, following this blog post, you would do something like this:

Problems:

  1. Where do you set the listener?
  2. Where do you unset the listener?
  3. Where do we maintain all these open listeners and how we prevent duplicate listeners.
  4. Doing .on(‘value’, callback) gets all messages on every change (even to a single message) which is wasteful.
  5. How do we report progress while fetching the data?

We’ll start with problem 5, for which a pattern to handle it exists in Redux documentation. Basically you have to create two actions creators and one async action creator that puts them all together (if you don’t know what I’m talking about please go back and read Redux documentation). This will also require handling the new actions in the appropriate reducer.

Thus we get the following pattern:

Under actions we’ll have something like:

and in our reducer:

This works well, but it doesn’t scale. It gets tedious to do every time you want to add an interaction with Firebase.

Proposed solution — make generic actions:

and reducer:

Lets continue to problem 4, instead of doing a listener on value we can do a child_added and/or child_changed listeners which only retrieves the child data. The thing about child_added is that it is triggered once for each existing child and then again every time a new child is added. Usually it makes sense to bring all the data once, and only then handle child events.

Based on this stackoverflow answer:

We propose — use the following pattern (notice the usage of inProgress):

and in our reducer:

This looks like a lot of code, but remember you have to do it only once, and then adding a new database interaction is a single line function that wraps the listenToPath action such as:

Ok, let’s discuss a little bit about problems 1,2,3. The pattern we’ve seen in most places is adding the listener on componentDidMount, saving the listener ref as a component property and removing the listener in componentWillUnmount.

This works for most cases (we’ll talk about it later), but the thing that bothers us here is that the component is no longer ‘pure’ as it holds the listener instance.

Proposed Solution — Keep the reference in the Redux store:

Update the listenRequestedaction to:

Update the listenToPath action to (these are just the relevant lines):

Then we can create another set of action creators:

And the reducer:

We also recommend removing the listener every time before adding a new one, just to avoid duplicate listeners in case you forgot to do it in componentWillUnmount.

So what are the issues with adding listeners in componentDidMount?

  1. Lacks control of data fetching. For example lets say we have a chatting app, where a user have several chats, and the chat component mounts/unmounts when a user enters a chat. It seems redundant extracting the same data over and over again (though Firebase caches data, we still do a lot of redundant actions). Also what happens if we would like to pre-fetch the most recent chats for better experience?
  2. In our application we have a screen that is accessible to anonymous users and signed in users. The screen displays the same data for anonymous and signed in users and remains mounted when a user signs in.
    The thing is, when a user signs in you’ll lose your current database listeners due to the authentication change, so unless you re-add the listeners you won’t receive any new data.
    This means that we need to add code in componentWillReceiveProps to detect changes in authentication state and remove/add listeners accordingly. And we have to do it on each relevant component (which is also hard to figure out).

Proposed Solution — Middleware

We haven’t implemented this fully yet, but expect a blog post about it :)

Basically, a Middleware lets you hook into state changes (before an action is processed by its reducer) and dispatch actions accordingly. We’re planning on hooking into navigation and authentication state changes and setup our listeners based on the previous and next state.

That way we can keep our components pure and avoid spamming them with logic which is not really relevant for them.

This was a long read, but we think that most of the problems described above are inevitable when using React Native, Firebase and Redux, and are hoping we’ve propagated some value to whoever reads this.

You can see the patterns described here (and much more) in our repo:

https://github.com/rmrs/react-native-redux-firebase-patterns

Many thanks to Omer Levy and Sagi Kedmi that were and are an integral part of the work that lead to this post, and for their help in reviewing it.

--

--