I create UI. It works well. But i don't know how to send data from Java to JS? In react native moduls I can use callback and activete this onClick events. But in UI i don't know.
More about what I need.
I have android component. Send it to JS this way createViewInstance(ThemedReactContext reactContext)
and users something change inside component. And these changes I see in java class. I need to send these changes to the JS when JS ask for them.
You know how to send data from UI component to JS? Please give me some example. Thank you.
Building on gre's answer that put me on the right track, but still left me with a lot of work to do, I'll try to explain some of the missing details.
There are 2 basic ways to do this:
As gre mentioned, the React Native docs explain this in the Native UI Components section of Events.
They show how to send the event using the following code:
WritableMap event = Arguments.createMap();
event.putString("message", "MyMessage");
ReactContext reactContext = (ReactContext)getContext();
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
getId(), "topChange", event);
With this explanation:
The event name
topChange
maps to theonChange
callback prop in JavaScript (mappings are inUIManagerModuleConstants.java
).
The actual definition from UIManagerModuleConstants.java
looks like this:
"topChange",
MapBuilder.of(
"phasedRegistrationNames",
MapBuilder.of(
"bubbled", "onChange",
"captured", "onChangeCapture")))
i.e. by using the event topChange
in the Android code, you can intercept it in JS with either onChange
or onChangeCapture
due to this mapping.
You can find many other existing events declared in there to piggy-back on.
There are also "direct" events declared in that may be more useful:
"topLayout",
MapBuilder.of("registrationName", "onLayout")
i.e. Android event topLayout
maps to JS event callback onLayout
(I do not understand the difference between the "bubbled" vs "captured" vs "direct" event types)
To receive the event in JS, take note of the 3 places _onChange()
is referenced in the docs:
create the callback method: _onChange(event: Event) {}
bind it in the constructor: this._onChange = this._onChange.bind(this);
pass it when creating your custom view: return <RCTMyCustomView {...this.props} onChange={this._onChange} />;
Custom events need to be declared to the system before they can be used, mapping Android events to JS events in a similar way to how they are done by React Native above.
This is done by overriding one of the following methods in your ViewManager
:
getExportedCustomBubblingEventTypeConstants()
getExportedCustomDirectEventTypeConstants()
The javadoc is very helpful in showing how the mapping should work, but I found it useful to reference the React Native code in UIManagerModuleConstants
mentioned above.
Once it is declared there, you use it as you would any other "existing" event.
I wanted to send the click event from Android up to JS, and to call it onClick
. I chose to use a "direct" event for this. I also chose to use the same name in Android and in JS - this is not necessary.
3 files need to be modified:
ViewManager class
This code maps the Android event name "onClick" to the JS function "onClick".
/**
* This method maps the sending of the "onClick" event to the JS "onClick" function.
*/
@Nullable @Override
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
return MapBuilder.<String, Object>builder()
.put("onClick",
MapBuilder.of("registrationName", "onClick"))
.build();
}
View class
This code sends an event to the JS when the view is clicked. The name used here is the Android event name, which will map to whatever you set above in the ViewManager
class.
// trigger the onPress JS callback
super.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final Context context = getContext();
if (context instanceof ReactContext) {
((ReactContext) context).getJSModule(RCTEventEmitter.class)
.receiveEvent(getId(),
"onClick", null);
}
}
});
The instanceof
check is there because this view is sometimes referenced from native code, outside the React context.
React Native JS component
Bind in constructor:
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
Declare actual callback function:
onClick(event: Event) {
// do something
}
Make sure to set the callback when rendering your view:
render() {
return <NativeView onClick={this.onClick} />;
}
All very simple when laid out like this, but the documentation of the finer details is scattered around the web.
http://facebook.github.io/react-native/docs/native-components-android.html#events
^this shows how you can trigger events from Java side to JS on an UI component.
but for "custom events" (events that are not pre-defined likes onLoad, onScroll, etc..) you will also need to override getExportedCustomDirectEventTypeConstants
.
Here is an example, triggering onGLProgress for gl-react-native:
(1) define the custom event mapping: https://github.com/ProjectSeptemberInc/gl-react-native/blob/7d6e83de5a8280d06d47234fe756aa3050e9b9a1/android/src/main/java/com/projectseptember/RNGL/GLCanvasManager.java#L115-L116
(2) dispatch the event from Java to JS: https://github.com/ProjectSeptemberInc/gl-react-native/blob/0f64a63fec2281e9d6d3641b9061b771a44fcac8/android/src/main/java/com/projectseptember/RNGL/GLCanvas.java#L839-L849
(3) and on the JS side, you can give a onGLProgress
prop callback.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With