As the React Native team is working on the
new architecture, there have been a few terms used to describe the various pieces. This post aims to clarify some of the terms and points to places in the repository with relevant code.
The Bridge
In the current architecture of React Native, the communication between JavaScript and Java/ObjC happens over "the bridge".
- This bridge is a queue to send messages encoded as JSON strings between JavaScript and Java/ObjC. During every tick, we dequeue messages from the front of the queue and process them. This way of messaging is fundamentally asynchronous.
- The bridge also exposes an interface for Java/ObjC to schedule
JavaScript execution, typically used for callbacks from Native Modules.
- The bridge is also tied to the lifecycle of React Native. Starting or stopping React Native usually means that the bridge is initialized or torn down.
To explore the bridge in more concrete terms, we can install a
MessageSpy to look at the exact bytes that are sent back and forth. Also note that while the bridge is async, we can use
@ReactMethod(isBlockingSynchronousMethod = true)for one-off
synchronous method calls.
While this asynchronous communication is great in most cases, there are certain use cases where we would prefer JavaScript to draw views on the screen synchronously; a problem that the new architecture aims to solve.
RPC
) Java/ObjC methods.
An analogy would be how we call DOM methods from JavaScript in the browser. For example, in the statement
var el = document.createElement('div'); the variable
el holds a reference not to a JavaScript object, but to an object that was possibly instantiated in C++. When JavaScript calls
el.setAttribute('width', 100), we end up synchronously invoking the
setWidth method in C++ that changes the actual width of that element.
In React Native, we can similarly use the JavaScript interface to invoke methods on UI Views and Native Modules that are implemented in Java/ObjC.
The snippet below shows a simple usage of JSI and how we could expose Java/ObjC objects to JS.
Most of the code for JSI resides in the
jsi folder in React Native and is written in C++.
Fabric
Fabric was the first part of the re-architecture that was announced. While it only deals with the user interface of the new architecture, it is sometimes wrongly used to refer to the entire re-architecture work.
In the current architecture, all UI operations (like creating native views, managing children, etc). are handled by a native module called
UIManagerModule. The React Reconciller sends UI commands over the bridge, which are eventually handled by this module and delegated to
UIImplementation. This in turn creates
shadow nodes that represent the layout tree and are passed to Yoga to determine the relative co-ordinates based on the Flex box styles that are passed in from JS.
In the new system, the UI operations are
directly exposed to JavaScript as functions using the JSI interface described above. The new
UI manager can then create
ComponentDescriptors and the Shadow Nodes for specific view types (like Text, View or Images), and then communicate with Java/ObjC to draw platform specific UI.
TurboModules
The JSI system can also be used to call leverage device capabilities like bluetooth or other sensors by exposing functions that JS can call. This is similar to how browsers expose functions like
navigator.geolocation.getCurrentPosition that, when invoked in JavaScript, trigger the respective C++ call in the browser.
In the current system, a
table with information about module names and methods is created. When JS calls a specific native module, the indices of the module and methods are passed to Java/ObjC, which then
invoke the specific methods. The arguments and return values are also
converted between JavaScript and JNI/ObjC objects.
In the new system,
- We expose a JSI object a top level "Native Module Proxy", called global.__turboModuleProxy.
- To access a native module, say SampleTurboModule, application code will then call in require('NativeSampleTurboModule').
- Inside NativeSampleTurboModule.js, we call TurboModuleRegistry.getEnforcing() which then calls the global.__turboModuleProxy("SampleTurboModule")
- Calling global.__turboModuleProxy function triggers the JSI function that we exposed in Step 1. This is where the platform divergence happens.
- We invoke a getModule function that is defined for Java and ObjC. This function takes in a string, and returns a JSI object for the specific TurboModule.
- To get a TurboModule JSI object, we first get the Java/ObjC implementation and then create JSI object from it.
Now that we have a JSI object for "SampleTurboModule", can invoke methods on this JSI object from JavaScript. During the calls, we also need to convert JSI Values to JNI for argument parameters, and the reverse when sending back results.
Like in the current architecture, most types including boolean, strings, Maps, Arrays, Callbacks and Promises are supported.
CodeGen
In both TurboModule and Fabric, interface available to JavaScript could be defined using
Flow (or TypeScript). We can further leverage this interface definition to generate many of the C++ classes, and the interfaces/protocols for Java/ObjC implementations. For example, in case of TurboModules, the C++ class that wraps the Java/ObjC class and exposes the methods using a JSI object can be generated.
This will ensure that all JavaScript calls have implementations available on the native side, and will continue to ensure this with over the air updates like code push.
Conclusion
In terms of backward compatibility, most of the JavaScript application code does not have to change as a result of the new architecture. The Java/ObjC code written for custom View Managers or Native Modules will have to change, but many of them can be code-modded to use the new system. A compatibility layer can also be written that will let custom View Managers and Native Modules to continue working in the new system.
In terms of timelines, most of the JSI code has already landed in the repository at the time of writing this post. A lot of the Fabric code is also in the repository, and updates to TurboModules continue to roll out. Since this is a mostly backward compatible, there does not have to be a single date of release, but more of a gradual rollout. You can follow the React Native repository, and the issues about
Fabric and
TurboModules for updates.