Building an app with Facebook’s React Native: It’s ready!

By Mark McKenna and Calvin Lam

Thoughtworks Canada
Connected
9 min readApr 25, 2016

--

React Native is an open source framework that allows you to build native iOS and Android applications using JSX: a JavaScript syntax extension that looks similar to XML. React Native is developed by Facebook and inspired by their React web library.

Although it leverages core web technology, React Native does not use webviews for UI; instead it uses real native views. React Native isn’t limited to building views: many native APIs can be leveraged by React Native, such as networking, camera access, and push notifications. Despite React Native still being in beta, Facebook has used it in Facebook Groups, Facebook Ads Manager, and their recently open sourced F8 app.

When you might want to consider working with React Native:

  • Your app needs to support iOS and Android. The majority, if not all, of your React Native code can be shared between iOS and Android. This means you can build your app faster. Having a centralized codebase will make your app easier to maintain.
  • You might need to add support to either iOS or Android in the future.
  • Your engineering team has experience with React, or has front-end web experience. Developers don’t need mobile development experience to hit the ground running since React Native is based on React web, JavaScript, and CSS.
  • You already have a natively built iOS and Android app, but you’d like to quickly add new functionality to both platforms. It is possible to embed React Native views into existing native apps.
  • Your app has a simple UI. It is surprisingly easy to whip up basic UI views such as list and detail views in React Native.

Things to be wary of:

  • Complex views. If you’re relying heavily on native functionality that’s not supported by React Native, then you’ll find yourself bridging (i.e. porting native UI components to React Native) frequently. For example, if your app needs to support rendering complex graphs or sophisticated animations, you’ll probably need to implement that logic natively on iOS and Android and make it available to React Native. React Native has handled bridging over native logic and views well, but there is a learning curve and it obviously requires more effort.
  • Dependencies on native libraries. Bridging also supports exposing native APIs to React Native. A lot of APIs are supported by React Native out of the box. If the APIs you need are not supported you’ll need to add support yourself via bridging. As we mentioned earlier, bridging native code takes more effort than writing it directly in React Native.
  • Source control woes. Your codebase will contain 3 components: your React Native code, your iOS code, and your Android code. The more complex a codebase, the higher the chance that files will get out of sync or dependencies will be missed between your engineers. This can cause great consternation as your engineers scramble to figure out why your app is building on one of their machines but not another.
  • Frequent React Native updates. React Native is still in the early stages of development, so expect the library to change frequently and account for the effort required to keep your React Native codebase up to date.

What we built with React Native

As a rapidly growing product development company, Connected Lab has a lot of moving parts: we need to manage clients, contracts, projects, and employees. Instead of inputting this information into a bloated and unwieldy spreadsheet, we decided take a more elegant approach by building an internal web application; we call it Allocations. The main features of Allocations are as follows: list current projects, display the contract status of each project, indicate who is managing each project, and indicate which engineers are working on each project.

We used React Native to build a native iOS and Android app for Allocations, and we would like to pass on what we’ve learned to you!

Allocations: built with React Native

React Native Development Flow

So what is it like working with React Native? For one thing, you’ll be using a local server to serve up your JavaScript code via React Native’s package manager. Here’s how it works:

  • Write your project in React Native using JavaScript.
  • Run the iOS simulator or Android emulator.
  • The root view of your iOS or Android project will be a React subclass, which is instantiated by a JavaScript bundle fetched from your local machine running React Native’s packager. Your root react view creates a hierarchy of native views from the JavaScript bundle.

What we liked

Live reloading

There are many tools available with React Native that provides significant advantages over developing in native code especially with the UI. If you’re accessing your local server while developing on a device or on a simulator, you’re presented with the ability to reload your Javascript code on the spot which is extremely useful while developing UI. Shaking your device or pressing the menu button will bring up further valuable tools as you see in the screenshot. One of these tools we found especially useful was the ability to enable Live Reloading. Where enabled, the currently active page on the device/simulator will reload from the local server whenever the developer saves a JavaScript file. This is especially exciting for developers to see their changes live in multiple devices on different platforms while only working on one single codebase.

In contrast, native development on both iOS and Android have been notorious for slower compilation time compared to other forms of development, especially with more complex projects. In order to add new UI elements or pixel pushing in general, we would have to wait for the entire project to compile and run every time we want to see our changes reflected on an actual device.

UI Inspector

Another unique tool React Native developers should take advantage of is the Inspector Tool. This tool allows developers to click on view components to highlight and inspect its current style properties (eg. margin, padding, size, color). Adjusting each UI component in our Allocations app to match the mocks was pretty simple with this tool as it provides a complete analysis of each component’s properties as well as the entire view’s hierarchy. For example, we can immediately tell whether our “TouchableHighlight” has a 15dp margin or “ProjectListCell” has a 15dp padding with the Inspector Tool as you can see in the screenshot above. With this information we’re able to easily analyze and debug each view with knowledge of its exact properties.

Performance Tool

In order to track performance with React Native we used their Performance Tool to analyze the current native and JavaScript frame rates. All business logic and views written in Javascript are rendered under one Javascript thread so we highly recommend having this tool enabled often during development. While we were developing our Allocations app, we noticed a huge dip in frame rate per second between page transitions after each swipe. Using the JavaScript frame rate as a benchmark, we were able to easily verify each incremental fix to our view paging performance on both Android and iOS.

How to use React Native effectively

Share modules to reduce fragmentation

Through our experiences with native modules, we learned to keep the style consistent as much as possible to avoid fragmentation throughout the app. One good example was when we decided to use both Navigator and NavigatorIOS for navigation in a single app. At first it seemed like a viable choice because NavigatorIOS provides the native toolbar and provides native iOS navigation animations as it leverages the UINavigationController class. However as the app became more and more complex, we found that we’d been generating an unnecessary amount of duplicate code while handling navigation for both platforms. In the end we decided to go with just pure Navigator because it gives us more flexibility on exactly how we handle navigation including how we pass properties and define the animations between pages. This way we’d only have to write the navigation code once for both platforms.

The same logic applies to importing native modules/UI into your React Native codebase. All properties and methods with shared functionality should always share the same name between both platforms. If you do that, you can avoid fragmentation when using these modules in your Javascript code.

Otherwise you’ll have to manage multiple pairs of native components in different ways with mostly similar functionality. You would likely be making platform checks via if-statements all over your codebase, which we all know is a pain to maintain.

Avoid poor performance with lists

There are some performance pitfalls with React Native. One notable one we found for iOS is that the ListView component is not built with UICollectionView. Instead of leveraging UICollectionView’s ability to reuse view cells as the user scrolls down the list, React Native renders each cell individually without deallocating cells that have been scrolled passed. This means that you may get framerate drops if your cells are complex and you’re scrolling quickly (since they are continuously being re-rendered) and your memory usage will steadily increase as you scroll. React Native’s ListView has some properties you can tweak to circumvent the framerate issue, such as pageSize and scrollRenderAheadDistance. Something else to try is turning off development mode when you run the app. We had some performance issues on the main project list of Allocations which disappeared when we deactivated React Native’s development mode.

Check for updates often

Since React Native is still a relatively new platform and still in its beta stage, you should keep up to date with their changes. Facebook is not only rapidly adding new features, but also makes a lot of changes to current features. For example, importing static images prior to version 0.14 would require images to be stored in their respective project’s resource folders. In order to import the images you would have to do a require without identifying the location of the images (eg. require(image!appIcon)). However, Facebook decided to change image management to prevent having to manage two image sets and allow the developer to decide where the images are held instead by specifying the relative paths (eg. require(./images/appIcon)). As a result all of our image imports were no longer valid after the upgrade to 0.14, so it was a good lesson to thoroughly read through their changelogs before deciding to perform the update.

If you don’t keep up to date with the current React Native version, you could easily be writing broken or deprecated code throughout your app which would become gradually more difficult to refactor. Facebook usually provides warnings regarding potential changes to their APIs at least one release ahead of time to give developers the time to refactor their code. There are plenty of improvements and new feature sets in each release as well. Make sure you prioritize your work so that you’re not reinventing the wheel for features that will eventually be available in a near future release.

The Future of React Native Development

React Native is already mature enough for production: just look at Facebook’s React Native showcase or our Allocations app for proof. What’s exciting is that Facebook seems serious about fully developing React Native into a robust alternative for mobile development. Since React Native is open source, we can expect significant community contributions as well. Here are a few recent developments in the React Native scene:

We’re hiring!

Currently undergoing rapid growth (and we mean rapid), Connected Lab is looking for talented engineers, designers, and product managers to join our team. Click here to view our open positions.

Stay connected by signing up for our mailing list here, and thanks for reading.

--

--

Thoughtworks Canada
Connected

Creating extraordinary impact on the world through our culture and technology excellence. ry, while advocating for positive social change.