The existing answer works great if you only need to support Android, but I found it didn't work when I was trying to integrate with iOS as well. I burnt quite a lot of time trying to wrangle this method into iOS, so I'll recommend what I came up with instead: using the UIManager
that comes with react-native.
React Native Component
// ComponentWithNativeFunctionality.js
import {UIManager, findNodeHandle} from 'react-native';
class ComponentWithNativeFunctionality extends React.Component {
const myRef = React.createRef();
functionToCall = () => {
UIManager.dispatchViewManagerCommand(
findNodeHandle(this.myRef.current),
"nameOfFunctionToCallInNativeLand",
[/* additional arguments */]
);
}
render() {
return <NativeComponentView ref={this.myRef} />
}
}
Android
// YourViewManager.java
public class YourViewManager extends SimpleViewManager<YourView> {
// ...
@Override
public void receiveCommand(@NonNull YourView view, String commandId, @Nullable ReadableArray args) {
super.receiveCommand(view, commandId, args);
switch (commandId) {
case "nameOfFunctionToCallInNativeLand":
view.nameOfFunctionToCallInNativeLand();
break;
}
}
}
}
iOS (with Swift)
- Add
#import "React/RCTUIManager.h"
to your Bridging-Header.h
// YourViewManager.m
@interface RCT_EXTERN_MODULE(YourViewManagerClass, RCTViewManager)
//...
RCT_EXTERN_METHOD(
nameOfFunctionToCallInNativeLand: (nonnull NSNumber *)node
)
@end
// YourViewManagerClass.swift
@objc(YourViewManagerClass)
class YourViewManagerClass: RCTViewManager {
@objc func nameOfFunctionToCallInNativeLand(_ node: NSNumber) -> Void {
DispatchQueue.main.async {
let component = self.bridge.uiManager.view(
forReactTag: node
) as! MisnapCameraView
component.nameOfFunctionToCallInNativeLand()
}
}
}
Another note: you can't pass in a Promise like you can with modules. You will have to pass in a unique ID generated in JS, and then once your action is done, fire an event to bubble back the result to JS with the ID attached to the event.