A deeper look into React Native and its architecture, Part I
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
To the point: The current react native architecture has three main realms,
- React Native Bridge, and
- The Native Realm (Native Thread)
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 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:
- Whenever our application is launched, the underlying OS assigns a single main/native thread to our program.
- 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
- 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,
- 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!