A Simple React Native & Firebase App

A fun little mobile app I built in the sun, for multiplayer scorekeeping

If you want to follow along with the source code, it's available on GitHub.

When I'm taking some time away, I try and forget about everything going on back at work and at home - it's often the only way I'll return actually feeling refreshed. To that end, I delete Slack from my phone (too much temptation to check it - if anything really urgent happens, my teams have my number), and while I usually do bring a laptop with me, it's loaded with games and movies. Any coding I do will be a brand new project, for the fun of it, and is likely something I will throw away upon returning home.

To that end, while spending the other week by a pool in Cyprus, I wanted to stretch my coding muscles a little, and spend a few hours building something just for the joy of it (and it wouldn't hurt to learn a little something, too). One evening over dinner, the idea came to me: a small Android app for my partner and I to keep score on arbitrary things together - how many gin and tonics we'd each had, how many margaritas we'd each had, how many caipirinhas we'd both had - that sort of thing. We have a competitive relationship.

Did I mention I was on holiday?

After dinner - and a couple more G&Ts - I settled down on the verandah to write some code. Initially I began with Kotlin, but quickly got bored of dragging and dropping things around in Android Studio, so decided to switch it up a little bit with React Native. Here's a screenshot of what I ended up with:

The build

After bootstrapping the application with create-react-native-app and getting it running in the simulator, I built up a simple local data model to make a proof of concept of the app. I could have just used React components' internal state - the app is simple enough - but wanted to try out Govern. This is something James Nelson has recently built, and I enjoyed his articles about state management in React enough (seriously, check them out :neckbeard:) that I wanted to give his solution a go.

Govern's API is really familiar to anyone who's used React before - as you're essentially using React components to compose a model, rather than a view. The main difference is your render() function just returns a JS object, rather than rendering JSX.

import * as Govern from 'govern'

export default class CounterModel extends Govern.Component {
    static defaultProps = {
        score: 0,
    }

    increment() {
        this.setState({
            score: this.state.score + 1
        })
    }

    render() {
        return {
            score: this.state.score,
        }
    }
}

Then, when rendering a component, you can tell it to subscribe to a Govern component's state, and pass in state and methods as props. Here's a simplified version of one of my components:

import React from 'react'
import { Button } from 'react-native'

export default (props) => (
    <Button onPress={props.increment}>
        <Text>Score: {props.score}</Text>
    </Button>
)

And here's how it's called, wrapped in Goven's <Subscribe> component:

// ...
<Subscribe to={<CounterModel />}>
    {counterModel =>
        <Counter
            score={counterModel.score}
            increment={counterModel.increment} />
    }
</Subscribe>

It's all very simple and clean, with minimal overhead or setup - but still allows for a very clear separation between your UI and where state & logic are stored. Before long, I had a working prototype. Swim break!

Many lines of code were written, books read, rays absorbed, and laps done here.

Enter multiplayer

Next, how to persist the data across multiple devices. At this point, the application was functioning, but state was local to an application instance. Not great for multiplayer - plus whenever the app relaunched, the counters were reset to 0. I didn't want to spend time building out a backend, however simple - that would detract from the real project, and infringe on my valuable sunbathing time - so I decided to give Firebase a go.

Firebase was purchased by Google a while ago, and provides, amongst other things, a realtime database and backend as a service - essentially a hosted NoSQL store with a realtime pub/sub model. I've thought it sounded pretty cool since I saw Monica Dinculescu's talk about it (@notwaldorf), so figured it couldn't hurt to try it for this project. (By the way, do yourself a favour and spend the 20 minutes to watch that talk - it's so delightful and inspiring! 💚)

Integrating this actually turned out to be the simplest thing on the planet: pass it your credentials (and keep them out of version control!), and then call syncState to, well, sync your component's state with Firebase. You can then continue working with state in the usual way, only now, it's seamlessly persisted to the cloud 😘👌. Because we're using Govern, too, the state is isolated within our model anyway - so we even only need to put this in one place:

import * as Govern from 'govern'
import Rebase from 're-base'
import base from '../rebase'

export default class CounterModel extends Govern.Component {
    static defaultProps = {
        score: 0,
    }

    componentDidMount() {
        this.ref = base.syncState(this.props.collection, {
            context: this,
            state: 'score',
        })
    }

    increment() {
        this.setState({
            score: this.state.score + 1
        })
    }

    render() {
        return {
            score: this.state.score,
        }
    }
}

Installing the app

So far, I'd been solely working with the app from the Android device simulator. create-react-native-app, at time of writing, uses a tool called Expo, which is an additional abstraction layer over React Native itself to makes it easier to get started - in their own words, it's "kind of like Rails for React Native". Expo provides a bunch of quality-of-life CLI tools (exp), and also runs a server locally to allow simple testing of your application during the development phase. The downside is that if you want to integrate with native code, you have to "detach" your app from Expo (permanently) - which limited my choice of Firebase library, in fact - but I found it provided a nicer developer experience than the last time I tinkered with React Native, so for a project like this I was happy with the trade-off.

Expo also provides a free cloud-based build system for your application. Running exp publish:android (or ios) starts your application building (during which time I had another swim), at which point you're able to download the published .apk (or .ipa) to install on your device or submit to the appropriate store. This was seamless - one command and I was done. Publishing updates is similarly so (for example, I later added an isLoading state to avoid local state and Firebase getting out of sync); in fact, as your app by default downloads the latest JS bundle at launch from Expo, there's no extra install step unless you change assets or something - once the build is finished, simply open the app and you have the latest version. Take that, app store!

(Just to remind you of my luscious surroundings while I built and deployed this app!)

Learnings

There've been a couple of articles recently about various companies leaving React Native and going back to true-native code. The problems they faced seem mostly cultural - in both cases, it looks like they put native mobile engineers to work on what is effectively a React codebase, whereas I think putting JavaScript engineers on your application makes so much more sense here. There's definitely much more JS knowledge required to build a React Native app than there is Java / Kotlin / mobile expertise, and in the same way that the web and (unfortunately) Electron have begun to subsume native desktop application development these days, I can see the Katamari that is JavaScript rolling over the mobile space in the near future, too.

So should you use React Native? Obviously it depends on your app or project. If you're building something high-performance, like a game, then React Native simply isn't going to work for you. If your entire business model revolves around people using your app for long periods of time, and relies on your app being as buttery-smooth to use as possible, then I can see why you might not consider it either. I'm not sold on AirBnB's decision to move away from React Native, but as I see it this is the cause for their decision, and it's not unreasonable.

But mostly, apps are a bunch of ListViews, with some pictures thrown in and a little server integration. For these cases - especially when, like in so many cases, your website is your main conversion driver, and the app is an extra part of the offering - then my advice would be to absolutely not discount React Native. I know I'll be using it in a few, more professional, projects.