question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

No way to access React instance of a pure C++ native module in order to communicate back to JS

See original GitHub issue

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:

  1. 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)

  2. 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:

  1. The C++ module inside the RCTCxxModule that I get back from [bridge moduleForName...] has no React _instance set (it is nullptr), so I have no way to call back to JS (as it just crashes when trying to callJSFunction). 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:closed
  • Created 5 years ago
  • Comments:13 (10 by maintainers)

github_iconTop GitHub Comments

1reaction
chirag04commented, Mar 22, 2018

@mhorowitz would know better.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Communication between native and React Native
Native modules are Objective-C classes that are available in JS. Typically one instance of each module is created per JS bridge. They can...
Read more >
React Top-Level API
React components can be defined by subclassing React.Component or React.PureComponent . If you don't use ES6 classes, you may use the create-react-class module...
Read more >
Connect | React Redux
The connect() function connects a React component to a Redux store. It provides its connected component with the pieces of the data it...
Read more >
Getting started with React - Learn web development | MDN
For instance, React Native can be used to build mobile applications. To build for the web, developers use React in tandem with ReactDOM....
Read more >
How To Set Up a React Project with Create React App
You can start writing React code with minimal preparation. By the end of this tutorial, you'll have a running React application that you...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found