A deeper look into React Native and its architecture, Part I

React Native is a framework that allows the development of robust cross platform applications using the popular JavaScript library, React. This means developers will rely on already existing knowledge on JavaScript and use it to make native applications with out the need to learn their respective languages (Java/Kotlin for Android, and Objective C/Swift for IOS).

Setting up the development environment

Before we take a look at how React Native works under the hood, let’s briefly go over what goes on when we first initialize a react native app.

There are two ways of going about the set up process according to the documentation.

Method I (React Native CLI)

The React Native CLI is a built in feature of react native. Upon initialization, it gives us access to the native modules by making the android and ios folders available in the project folder. This allows us to take full control and get our hands dirty with some native code if need be.

Once set up, you can run your application on an android emulator or ios simulator and get started with your project. You can also use USB debugging to test on physical devices.

Note: This configuration requires Android Studio and XCode to run and bundle your android or ios apps respectively.

Method II (Expo CLI)

Expo CLI is a set of tools built on top of React Native. It is without a doubt the easiest way to get started with your project.

Once the set up is complete, you do not need to run an emulator or a simulator because expo provides an Expo Client app for both android and ios which then runs your app by directly scanning a QR code from your terminal.

It is also possible to build your application for release using Expo build with out the need to use XCode or Android Studio.

Note: This configuration does not give you control over the native application modules and is ideal for simple applications that does not require native features.

The Current Architecture

React Native allows us to get close to native application development much better than hybrid application developments do. This is because the program we write in JavaScript is, at the end of the day, compiled to its native equivalent. Unlike in the case of hybrid development where by web applications are run inside a native environment by using a native app wrapper or container such as Cordova.

To the point: The current react native architecture has three main realms,

  1. JavaScript Realm (JS Thread),
  2. React Native Bridge, and
  3. The Native Realm (Native Thread)

The JavaScript and Native realms are completely isolated from each other, and their communication is possible only via the React Native Bridge.

React Native Architecture

When ever we run our app, the React Native CLI spawns up Metro which bundles all our .js files into one main.bundle.js file.

The JavaScript side of react native runs on a single JS Thread where all the code we write lives. This happens inside the JavaScript VM which by default uses a popular JS engine called JSCore to execute our application logic.

JSCore was made by Apple to execute JavaScript programs with in Swift, Objective-C and C-based apps. IOS devices therefore come with JSCore by default, but this is not the case for Android devices. This means that react native has to bundle the JSCore together with the app for android applications, which makes them a bit larger in file size (upto 3–4 MB).

The React Native Bridge is a carrier layer responsible for the interaction between the JS logic we write and the underlying native platform. All data is sent to this bridge asynchronously as serialized grouped message.

The Native side handles all interactions such as tap, scroll, or any incident associated with native features, and sends the data to the React Native bridge then over to the JS side so that our logic handles what ever needs to happen. UI logic is then rendered by the help of a Shadow Thread.

Let’s briefly take a deeper look:

The three threads of react native

  • Whenever our application is launched, the underlying OS assigns a single main/native thread to our program.
  • This main thread starts the JS Thread and Shadow Thread separately, then lets the JS thread handle all the JavaScript logic. This separation of threads is important because we want the main thread to be free to handle UI interactions from the user while the JS thread handles heavy calculations.
  • When something needs to be rendered on the screen, the shadow thread takes the flex box system compiled by JS and translates it to a system which the native host can understand. This is all possible by a cross platform layout engine called Yoga. The rendered markup from Yoga is then send over to the main thread to be displayed.

Keep in mind that all the communication between the Native Thread and JS thread is via the React Native bridge.

Problems with the current architecture

  • While React Native allows us to use native modules, say a Java library without the need to re-implement it in JavaScript, the disadvantage comes as it is constantly going to and fro the threads of JavaScript and Native environment,
  • Because of the asynchronous nature of the communication over the bridge, there is no guarantee whether the data will get to the other thread, or in time,
  • All data has to be serialized into JSON when sent to the bridge, and deserialized on its way out. This can take up valuable resource for heavy problems,
  • It takes time for the JS code to be parsed and executed inside JS VM,
  • JavaScript being a loosely typed language can be problematic to translate to native languages, but this can be solved by using TypeScript,
  • Debugging can be very difficult, and often requires knowledge of the native languages of the underlying platforms,
  • React native does not have access to all the native features, and additional modules are required to be able to invoke these methods (such as gestures and animations)
  • Other performance issues that are out of the scope of this article can be found here.

The React Native team are redesigning how react native works, and it is expected to solve most of the issues with the current architecture.

In the second part of this article, we will take a look at the new react native architecture. Coming soon!