How We Built Our React Native App

Sid Jain
Sid Jain
Sep 8, 2017 · 8 min read

An ideal mobile application should be an extension of the mobile web instead of being a replacement.


Challenges

  • We are building experiences on 3 different platforms, namely: Android, iOS and the web (desktop and mobile).
  • This means duplication of business logic across 4 codebases, which is not the best thing to do if you go by DRY.
  • It also means introducing new features or modifying existing features requires making the necessary changes across 4 separate codebases. This is not scalable at all and the platforms would soon end up being out of sync.
  • Finally, we would have to build and strategically expand 3 separate teams of developers for each of the 3 platforms.

Objectives

To overcome these challenges, we decided to place our bets on the newly emerging breed of cross-platform native apps built with a modern frontend stack in JavaScript. We began implementing the apps with the following main objectives:

  • The app should reuse as much code as possible across Android and iOS. This would be in line with the principle of DRY. It would also imply that maintaining the code is far easier and adding/modifying/removing features means touching the minimum number of files possible.
  • Last but not least, the stack used should be familiar to our team of product engineers for the web and the dependence on platform specific native developers should be reduced. This is also in line with increasing the bus factor at Housing.

Stack

  • react-navigation — still in its early days but it solves the much debated navigation issue in a declarative manner using the Animated API. It also fits well into our redux based state management system since it’s a purely JS based solution. However, we are investigating into other native and hybrid navigation solutions as well.
  • redux-observable — the JS ecosystem is still figuring the best solution to async state management but in the end, it is more of a ‘to each his own’ problem. We decided to use redux-observable because it helps us isolate side effects nicely and handle them with the expressive power RxJS operators. This approach also allows us to test our side-effects handling code in an isolated manner.
  • immutable — we faced nasty and hard to find bugs on previous platforms which arose from mutations caused in our reducers. To mitigate this issue for once and for all, we decided to use immutable data structures throughout the app. This was made possible by a custom reducer factory which converts between immutable and vanilla JS data structures.
  • ramda — as far as possible, we made it a point to code in a functional, declarative paradigm via pure functions which handle most of our business logic. Ramda has been irreplaceable for us in that regard.
  • redux-persist — Unlike web apps, native apps have a notion of offline mode and persisted state. This library along with redux-persist-migrate gracefully solved this problem with a backing AsyncStorage layer.

Tooling

Image for post
Image for post
Image for post
Image for post
Styleguide (Consistent across Android and iOS)
  • codepush — this is one area where react native apps really shine. We use codepush for releasing unobtrusive over the air updates to our users while completely owning the rollout percentages and target versions.
  • fastlane — managing different environments (staging, development, production) and automating our builds proved to be a breeze with fastlane. We exposed a parameterized build dashboard on our internal Jenkins CI which manages everything from app secrets, code signing, Test Flight and Crashlytics Beta uploads, registering devices for internal test builds, releasing OTA updates through codepush etc.
Automated End to End Tests in Detox
  • sentry — The folks at sentry.io introduced first class support for react-native apps sometime back. The new SDK enriches error reports with a lot of useful device specific data and provides holistic reports with both native and JS stack traces.

More than 90% of the app’s source code is in JavaScript while not compromising on performance and quality.


Learnings

React Native is a relatively young platform. The community around it is still deliberating on best practices and the right way to do certain things. As a starting point, however, the official docs are the best resource we have come across. Here are some things we learnt along the way:

  • requestAnimationFrame — This one is borrowed from the web and works identically. A particular use case is the ripple effect on Android devices. The usual approach of using aTouchableNativeFeedback with an apt onPress handler does not always work here. At times, you might not see the ripple. Instead, if you wrap your onPress handler in a requestAnimationFrame block, you’ll notice the animations are visible perfectly.
  • MessageQueue — React Native works by communicating between the JS and native realms over a bridge. As a result, there is constant chit-chat over this bridge which can affect performance adversely if not moderated properly. The spy method on MessageQueue , as the name suggest, lets you spy on this chit-chat and see what’s being sent across. This might help you understand what’s actually happening underneath and improve performance.
MessageQueue.spy(true)
  • Structuring — From the get go, we followed a simple organization structure for our repo. We separated our dumb UI components from stateful views. State management was all taken care of in our epics and reducers. We observed that randomly scattered side-effect generating code becomes the bottleneck in keeping our codebase performant and testable. Our approach with redux-observable helped us mitigate some of those pains. Consider the following example:

Build Pipeline

Image for post
Image for post
Build Pipeline

Pro-Tips

  1. Read the docs as well as the release notes.
  2. yarn start —- --reset-cache — for when you installed something and it does not work/can’t be found.
  3. react-native-debugger — The standalone app based on official debugger of React Native, and includes React Inspector / Redux DevTools.
  4. Make the bundled Perf Monitor your best friend.
  5. Always test on a real device.
  6. Knowing React is a pre-requisite.

Footnote

If this post got you excited about the kind of work we’re doing here, we’re hiring. Find us on Twitter @HousingEngg.

Engineering @ Housing/Proptiger/Makaan

Engineering and technology articles from developers

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

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