Go Deep into How React Native Interacts with The Bridge
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:
- How to detect that the react calls the Bridge?
- What data that send to the Bridge?
- What is the module that is used from the JS and UI Side?
- 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.
Let's see the result with debug mode in the opened browser.
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:
- Use JavaScript as the language doesn’t have a long compilation cycle time
- 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.
- Build a feature called Live Reload that reloads the app on save.
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.
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.
UIManager
This module will process creating a view in the native side.
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.
And if we collapse the topbar setting, it will send the javascript that the application state focus is true
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.
This is the object of the receiveTouches
That module will inform the javascript side about the location where we click it.
And if we scroll/click the screen, it will trigger receiveEvent
with this object.
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
When we added that code and now we can see the UI Manager process in the browser console:
- Text ->
Counter:
, the previous counter label. - Text ->
0
, because the initial counter is 0. - Button ->
Count
, the title on the button.
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
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.
Let's try to click 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
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.
After we added that, let's try clicking the counter again!
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 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'
.
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/