Content of the Gist is from a talk by Peggy Rayzis at Chain React 2017: Chain React 2017: Breaking Down Bridging in React Native by Peggy Rayzis
In order for a native code to talk to JS you would need a bridge.
- You need to integrate a third-party SDK.
- High perfomance is crutial
- You are building a brownfiel app
- You need access to a platform to a platform API. (Camera, Gyroscope etc.), You will need to use a opensource library or create a bridge.
Threading structure in React-Native.
NATIVE | JS
|
BRIDGE
MAIN THREAD (Responsible for UI) | JS THREAD
BRIDGE
SHADOW QUEUE (Layout calculations) |
BRIDGE
NATIVE MODULES (Each native module |
has it's own thread, while in case of |
Android they share a thread pool.) |
Javascript thread knows about all of your the native modules at run-time, they are injected into the javascript context as a global variable.
How it looks like:
{
moduleName: 'ExampleModule',
constants: {},
methods: ['ChainReact', 'bridgingIsAwesome'],
promiseMethods: [0],
syncMethods: [1],
}
ALL COMMUNICATION PASSES THROUGH THE BRIDGE
See 1st comment
All the messages are handled by something called as a messaage queue which has a native part and a Javascript part.
INTERACTION HAPPENS
|
NATIVE
MAIN THREAD --> BRIDGE -> JS THREAD
| | ^ |
SHADOW QUEUE <----- | |
| |
NATIVE MODULES <----- ------> MESSAGE QUEUE
(NATIVE & JS )
If everything is passed through this message queue to the javascript side, we need to make sure that the javascript thread is not blocked and that the message queue isn't congested(crowded).
MESSAGE QUEUE CONGESTION WILL SLOW DOWN YOUR REACT NATIVE APP.
If it is congested and the Javascript thread is blocked then we enter some weird situations. for example: We can scroll through the UI because it's done natively but javascript doesn't register our touch events. or other perfomance issues.
- Limit passes over the bridge. (Use Native Derive for Animations), first you sent the native configurations the first time so that you won't have to keep passing information across the bridge.
- Keep your data flat. (Try to avoid nested objects and arrays because you are just going to have conversion methods to use the data and native.)
- Keep data light weight. (don't try to send a image as base 64 strings over the message queue that's really going to slow the application down.)
- Write business logic in either native or JS.
- Debug with profiling tools & logging.
type BridgeData = {
type: number,
module: ?string,
method: string | number,
args: any
}
Every message passed over te bridge has this signature, whether it's a netword request or creating a View has this exact type signature.
type: number,
maps whether you are communicating from native to Javascript or Javascript to Native.
Tool that helps to see the messages we want to see is RN Snoopy.
iOS Android Windows
Objective-C Java C#
Swift Kotlin
- Most of the time, you're choosing one or the other MAIN THREAD
- Occasionally, you'll need to integrate both (e.g video player) SHADOW THREAD
- Integrating both is complicated because of how threading works NATIVE MODUES in RN
First you need to choose whether you need to build a native module or a UI component A native module does not render any UI it just exports methods and constants.
VIEW MANAGER IS A FACTORY FOR VIEWS
NATIVE JS
-------------- -----------
| | |
| Reference Stored on Bridge | |
| ----------------------------> | |
| | |
VIEW MANAGER | Require native component | REACT |
| | |
<---------------------------- | |
| | |
-------------- -----------
Whether you are building for iOS or Android you are gonig to construct a ViewManager. ViewManager orchestrates(Arrange/score) the interaction between Javascript and the View, it's a factory that creates your View and only onw instatce of the view manager is created per bridge.
The brodge knows about all the native modules(Read Above) at runtime, the view manager is like a special kind of native module except it returns UI and runs on the shadow queue. The bridge has a reference to it and it has information about the component name and any constants if you are exporting them.
If we want to construct a View we call the Require Native Component and that's what tells the view manager to create the View, it also checks the components interface via prop types to make sure that we are not passing in any incorrect types to our component.
NATIVE JS
-------------- -----------
| | |
| Reference Stored on Bridge | |
| ----------------------------> | |
| | |
VIEW MANAGER | Require native component | REACT |
| | |
<---------------------------- | |
| | |
| Sends Reference to View | |
| ----------------------------> | |
| | |
-------------- -----------
|
|
VIEW
Now, the View Manager creates the View it also stores the reference to the view instance on the bridge so we can use it on the javascript side and finally in order to display the UI we render our native View and just use it normally as we would any other react component.
NATIVE JS
-------------- -----------
| | |
| Reference Stored on Bridge | |
| ---------------------------> | |
| | |
VIEW MANAGER | Require native component | REACT |
| | |
| <-------------------------- | |
| | |
| Sends Reference to View | |
| ---------------------------> | |
| | |
-------------- -----------
| |
| RENDER |
VIEW <---------------------------------- COMPONENT
RECAP: UI COMPONENTS
- Only one instance of ViewManager is created per bridge, the ViewManager is a factory that cretes a View.
- ViewManager: Declare constants, set props, event handlers and create the view.
- When handeling events the ViewManager acts as a Delegate for all it's view instances when handeling events.
- View: Hook up lifecycle events & render children (subviews)
A native component is a special type of module that returns UI. (All native modules are global variables at runtime and represented by JSON [mentioned earlier])
NATIVE JS
-------------- -----------
| | |
| Reference Stored on Bridge | |
| ---------------------------> | |
| | |
VIEW MANAGER | Invoke Method on Module | REACT |
| | |
| <-------------------------- | |
| | |
| Sends data Back Via | |
| Callback Or EE | |
| ---------------------------> | |
| | |
-------------- -----------
The native side is what calls the method directly and if there is any information to pass back to javascript the method can do so either by a callback, a promise or sending an event through an event emitter.
RECAP: NATIVE MODULES
- Only one instance of the module is created per bridge, now this instance is initialized at run-time and if to communicate with JS.
- N-->JS: Callbacks, promises or emitting event.
- JS-->N: Method calls (call methods on the native modules and pass in any arguments which then transfers that message to the message queue.)
- Accessing constants is synchronous (Since javascript already knows abou the module constants at runtime accessing then is synchronous and you won't have to go through the normal bridging process.)
- Context switching between languages & files.
- So. Much Boilerplate. (You have to remember to subclass from certain RCT View managers or in the case of Android you have to override certain methods and it's kind on cinfusing to get all this boilerplate straight).
- Instructions are piecemeal.
https://github.com/peggyrayzis/react-native-create-bridge
cool!