One of the milestones of being a React Native developer is writing your first native module. At first, it may appear a daunting task, but the module part is easy to wrap your head around. Here are a few important points to keep in mind. The same applies to Cordova.
The bridge concept is not a unique to React Native. It is a common occurrence when you’re dealing with many languages or multiples platforms. It is how two different layers in a single system communicate with each other.
The main logic of the application resides usually in one of the layer, while the feature will be implemented in the other layer. The first will be the caller and the second will be the callee. For the two to communicate, there should be a common ground between the two, which we can call the host.
As the host is the one running the layers, it is trivial for it to customize their engine or interpreter. It will provide new functions in addition to the standard ones, which will be how the layer will get back to the host. Those functions may be arranged in a more cohesive set for a more intuitive use. It will replicate the caller layer’s data types in the callee layer.
A module is a plugin
Occasionally, the callee layer will need to execute before getting called, or even before the main layer is executed. In those cases, there will be a general framework to follow with hooks. The framework is to tell the host the functions to call, initialize or destroy the module. The hooks are there to add new features to the host without the other layer intervention (which can not access these parts directly in our case). These hooks are why we normally need a module in the first place.
A module is a library
But, how do we architect the module in the first place? From the host's perspective, it needs to be executable logic. While the host is initializing, it may run initialization code in the module. The same when the host is terminated. Logic in the module will mirror the host lifecycle. But from the perspective of the caller layer, it needs to be a library.
Ordinarily, the module purpose is to provide a new feature to the caller layer. It needs to be architected the same way as the libraries native to the caller layer. Which is to provide a consistent set of functions (API) for the caller layer to use. At the callee side, we need to mark those functions, and they need to follow the same signature (using the replicated data types).
The above is my mental model when writing a native module. The implementation will differ based on the platforms, but it provides a quick way to pick the relevant parts to learn from the documentation and example codes.