No way to access React instance of a pure C++ native module in order to communicate back to JS
See original GitHub issue- I have reviewed the documentation
- I have searched existing issues
- I am using the latest React Native version
I am trying to create a native module to communicate both from JS->Native and Native->JS in pure C++, in order to improve portability and reduce boiler plate of native code in our application (which has a large C++ component with lots of React Native integration going on).
There’s not much documentation out there about this so I have been figuring it out as I go, and have successfully managed to create a CxxModule
instance, based on SampleCxxModule
, which is registered via registerExtraModules
in my custom bridge delegate and can receive method calls from Javascript.
However, I’d also like to be able to call Javascript methods from C++, for example to emit events. I was hoping I would be able to do so by getting the module from the bridge a bit like:
TestCxxModule* module = [bridge moduleForName:@"TestCxxModule"];
module->emitSomething();
But there are a few issues I have found in doing so:
-
I can’t see a way to get the actual C++ module from the RCTCxxModule wrapper, unless I keep my own reference to it (which is fine, just seems a bit odd)
-
Unless I have accessed the module via JS, or call e.g.
[module methodsToExport]
, the module has not been lazily initialised when I try to use it
and the main one:
- The C++ module inside the RCTCxxModule that I get back from
[bridge moduleForName...]
has no React_instance
set (it isnullptr
), so I have no way to call back to JS (as it just crashes when trying tocallJSFunction
). If I put a breakpoint inside one of the methods exposed to JS, I can see when it is called from the Javascript context,_instance
is populated as I’d expect, but not when I get the module from the bridge.
For example, I’d like to be able to call this from anywhere in my app code (as long as I have a pointer to the module):
void TestCxxModule::emitEvent (dynamic eventData)
{
auto reactInstance = this->getInstance().lock().get();
reactInstance->callJSFunction("RCTDeviceEventEmitter", "emit", std::move(eventData));
}
I’ve followed the code which instantiates the modules and tried to work out if this is deliberate - I can see that in the module registry, the modules are wrapped inside a CxxNativeModule
which gives them the necessary context, but in modulesByName
inside the bridge, they are not. I don’t have deep enough understanding of the way RN is structured to know if this is deliberate or an oversight, and I couldn’t identify another way of getting this instance of the module with the _instance
set.
Is this expected behaviour, in which case is there some other way I can make JS function calls/emit events from C++ code? or it a bug, or am I perhaps initialising the module incorrectly?
I’ve created a simple repo reproducing the issue: https://github.com/tomduncalf/react-native-cxx-module – the module code is in https://github.com/tomduncalf/react-native-cxx-module/tree/master/ios/rnfresh/ReactNative, and if you look in https://github.com/tomduncalf/react-native-cxx-module/blob/master/ios/rnfresh/AppDelegate.mm#L27, you can see comments reflecting what I would expect to be able to do.
This seems to be fairly uncharted/undocumented territory so perhaps I am not hitting a bug but rather a misunderstanding, but any help would be welcome - it would be great to get some more documentation around doing this, and I’d be happy to have a go if I can work out the issues.
Environment
Environment:
OS: macOS Sierra 10.12.6
Node: 9.8.0
Yarn: 1.5.1
npm: 5.6.0
Watchman: 4.7.0
Xcode: Xcode 9.2 Build version 9C40b
Android Studio: Not Found
Packages: (wanted => installed)
react: ^16.2.0 => 16.2.0
react-native: ^0.54.2 => 0.54.2
Steps to Reproduce
See above and example repo: https://github.com/tomduncalf/react-native-cxx-module
Expected Behavior
Calling this->getInstance()
on a CxxModule
returned from bridge::moduleForName
would return a react::Instance
that I can use to make calls back to JS.
Actual Behavior
this->getInstance()
returns nullptr
so no communication back to JS is possible
Issue Analytics
- State:
- Created 5 years ago
- Comments:13 (10 by maintainers)
@mhorowitz would know better.
Have you seen this? https://github.com/sulewicz/djinni-react-native/tree/master/example-react-native/ExampleProject