Go Deep into How React Native Interacts with The Bridge

Didik Mulyadi
7 min readJul 31, 2023

--

Photo by Jonas Allert on Unsplash

We will talk about how React Native (v0.68) interacts with the Bridge. This is not the goal, the goal is about React Native Memoization, but to achieve that article, We should understand the fundamentals, so I created this article because I was curious about it, and at this point, I think nothing article explains it.

So, this article will answer these questions:

  1. How to detect that the react calls the Bridge?
  2. What data that send to the Bridge?
  3. What is the module that is used from the JS and UI Side?
  4. How Re-render works in React Native? Is it sending all of the element or component through the Bridge in every rendering?

To answer that question, read the fundamentals about how to react native works in my article https://medium.com/@didikmulyadi/measurement-react-native-performance-3da2fc7ac201.

This article is related to that, so I created this article because I want to improve the React native application performance.

Detect the Bridge

To detect the bridge, we can use a package from react native itself react-native/Libraries/BatchedBridge/MessageQueue.js . To implement it, just put it in your App.js .

import MessageQueue from 'react-native/Libraries/BatchedBridge/MessageQueue.js';

const spyFunction = msg => {
console.log({
...msg,
type: msg.type === 0 ? 'native -> javascaript' : 'javascript -> native',
});
};

MessageQueue.spy(spyFunction);

....
const App = () => {
....
}

export default App;

when the msg.type === 0 it means the data is going from native to javascript and msg.type === 1 it means the data is going from javascript to native. In that code, I replace the value to make it readable.

node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js

Let's see the result with debug mode in the opened browser.

All event when the app started

From that code, there are some modules, let’s focus on

HMRClient (Hot Module Replacement)

Based on React Native documentation, link. This module has three main
features:

  1. Use JavaScript as the language doesn’t have a long compilation cycle time
  2. Implement a tool called Packager that transforms es6/flow/JSX files into normal JavaScript that the VM can understand. It was designed as a server that keeps an intermediate state in memory to enable fast incremental changes and uses multiple cores.
  3. Build a feature called Live Reload that reloads the app on save.
HMR Module

WebSocketModule

For the Google Chrome debugger, React Native runs inside the JS VM in Google Chrome and communicates asynchronously with mobile devices via WebSockets. See the details, here.

Websocket Module

AppRegistry

AppRegistry is the JS entry point to running all React Native apps. it should be required early in the require sequence to make sure the JS execution environment is set up before other modules are required.

The App registry calls the runApplication method. That method is used for Loading the JavaScript bundle and running the app. The details of this API, link.

App Registry Module

UIManager

This module will process creating a view in the native side.

UI Manager module

RCTDeviceEventEmitter

This event is coming from the device when we have the interaction with the device. like we open the device topbar setting. It will inform the javascript that the application state focus is false.

Event from the device when opening the device setting

And if we collapse the topbar setting, it will send the javascript that the application state focus is true

Event from the device when collapsing the device setting and opening the app.

RCTEventEmitter

This event will notify the javascript side when we have an interaction in the application. in this case, I click on the text “Welcome to React Native”, even if it's not a link, it still sends the javascript side the information.

There are 2 methods called receiveTouches because we click the screen and receiveEvent because the click is part of an event.

http://localhost:8081/debugger-ui/

This is the object of the receiveTouches That module will inform the javascript side about the location where we click it.

when touch/click the screen

And if we scroll/click the screen, it will trigger receiveEvent with this object.

when scrolling the application

The RTCEventEmitter notify the javascript about TopScrollBeginDrag -> Scrolling -> TopScrollEndDrag. It is correct because we are using ScrollView, here is the props detail.

How Re-render Works?

I will try to create a button that works as a counter. This is the code

const App = () => {
const [counter, setCounter] = useState(0);
const increaseCounter = () => {
setCounter(prev => prev + 1);
};

...

return (
<SafeAreaView style={backgroundStyle}>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<View>
<Text>Counter: {counter}</Text>
<Button onPress={increaseCounter} title="Count" />
</View>
<ScrollView>
....
</ScrollView>
)
}

This is the UI

Example UI regarding the counter

When we added that code and now we can see the UI Manager process in the browser console:

  1. Text -> Counter: , the previous counter label.
  2. Text -> 0 , because the initial counter is 0.
  3. Button -> Count , the title on the button.
Javascript sending the event to build the UI to the UIManager
Javascript sending the event to build the UI to the UIManager

What happened when click the button?

We wanna try to click the counter, it should rerender the component. This is the log that we got

After clicking the count button

Because we click the screen it will call the receiveTouchers from the native to javascript, then we update the state which means, the javascript thread will send the event to the native side with the module UIManager and method updateView. The value changed from 0 to 1.

After clicking the counter, the value changed from 0 to 1

Let's try to click the counter again,

After clicking the counter again

Now, the javascript sending {text: '2'} to the main thread or UI thread, to update the view.

Let’s see the full log when we click the counter

full log when the user clicks the counter button from 0 to 1

Basically, React Native only re-renders the related component because in the code we only use the basic component from React Native.

Let’s create a new component

We want to see the component that we create with a class or functional component will be processed by UIManager or not. When the component is processed by UIManager, it means we need an optimization here.

Here is the new component and I put it it below the counter.

Greeting Card Component
App.js

After we added that, let's try clicking the counter again!

click 2 times, the counter from 0 -> 2

The Spy not detecting the new component that we created before, it means React Native already caches it.

Let’s put the title in the props name of GreetingCard

And try to click the counter.

The full log after add the counter in the props name

The Spy only has 2 requests to the native to update the UI. The first one is a request to update the counter text to 3, and the second is to update the name to'Didik Mulyadi3' .

details object of updateView

Conclusion

At this point, we should understand how the React Native interacts with the React Native Bridge. Every rerender will send the data from the JS thread to the UI thread through the Bridge, and only the component or element that has a change.

So what’s the benefit of React.memo, how React.memo interacts with the Bridge? I will create another article for it!

Thank you for reading my article.
Follow if you like. reach me on https://www.linkedin.com/in/didikmulyadi/

--

--