Respond to native user interface components: RCTBubblingEventBlock / RCTDirectEventBlock does not seem to work

I have my own view in the Ignite project. I am trying to customize a message from Objective-C to React Native . A post from React Native to iOS works with HTML injection, but not vice versa. I tried using both RCTBubblingEventBlock and RCTDirectEventBlock , but it does not work. Here is the completeness of my implementation. Of course, I changed the names of the components and simply left an essential implementation for your understanding of what has been done so far:

Objective-C code:

 // CustomViewManager.h #import "RCTViewManager.h" @interface CustomViewManager : RCTViewManager @end // CustomViewManager.m #import "CustomViewManager.h" #import "CustomView.h" #import "RCTBridge.h" #import "RCTEventDispatcher.h" #import "UIView+React.h" @implementation CustomViewManager RCT_EXPORT_MODULE() RCT_EXPORT_VIEW_PROPERTY(htmlInjection, NSString) RCT_EXPORT_VIEW_PROPERTY(onEventA, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onEventB, RCTDirectEventBlock) - (UIView *) view { return [CustomView new]; } @end // CustomView.h #import "RCTView.h" @interface CustomView : RCTView @property (nonatomic, assign) NSString *htmlInjection; @property (nonatomic, copy) RCTDirectEventBlock onEventA; @property (nonatomic, copy) RCTDirectEventBlock onEventB; @end // CustomView.m #import "CustomView.h" #import "RCTUtils.h" #import "RCTBridge.h" #import "RCTEventDispatcher.h" #import "UIView+React.h" #import "MyExternalComponent.h" @interface CustomView () <UIWebViewDelegate> @property (nonatomic, strong) UIWebView* webView; @end - (void) setUpWebView { if (!_webView) { [self setWebView: [UIWebView new]]; _webView.delegate = self; [self addSubview:_webView]; } } - (instancetype)init { self = [super init]; [self setUpWebView]; return self; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self setUpWebView]; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { if ((self = [super initWithCoder:aDecoder])) { [self setUpWebView]; } return self; } - (void) layoutSubviews { [super layoutSubviews]; CGRect frame = self.frame; self.webView.frame = frame; } #pragma mark - External methods. - (void) setHtmlInjection:(NSString *)html { [_webView loadHTMLString:html baseURL:nil]; } #pragma mark - Non-React component methods. - (void) fetchData { [MyExternalComponent getData:^(NSString *dataA, NSError *error){ if(error) { NSLog(@"Here be errors: %@", error); _onEventB(@{@"myError": error.localizedDescription}); } else { _onEventA(@{@"myData": dataA}); } }] } @end 

React Native JavaScript Code:

 // MyCustomView.js import React from 'react'; import { requireNativeComponent } from 'react-native'; class MyCustomView extends React.Component { constructor(props) { super(props); this._onEventA= this._onEventA.bind(this); this._onEventB= this._onEventB.bind(this); } _onEventA(event: Event) { if (!this.props.onEventA) { return; } this.props.onEventA(event.nativeEvent.myData); } _onEventB(event: Event) { if (!this.props.onEventA) { return; } this.props._onEventB(event.nativeEvent.myError); } render() { return ( <CustomView {...this.props} onEventA={this._onEventA} onEventB={this._onEventB} /> ); } } MyCustomView.propTypes = { htmlInjection: React.PropTypes.string, onEventA: React.PropTypes.func, onEventB: React.PropTypes.func, }; var CustomView = requireNativeComponent('CustomView', MyCustomView); module.exports = MyCustomView; // CustomWrapperContainer.js class CustomWrapperContainer extends React.Component { api: Object; constructor (props: Object) { super(props); this.state = { htmlInjection: '', myDataA: 'Some placeholder text' }; this.api = RestApi.create(); } render () { return ( <View style={styles.container}> <KeyboardAvoidingView behavior='position'> <Text>{this.state.myDataA}</Text> <MyCustomView style={styles.myStyle} htmlInjection={this.state.htmlInjection} onEventA={this.handleEventA.bind(this)} onEventB={this.handleEventB.bind(this)} /> </KeyboardAvoidingView> </View> ) } handleEventA = (data) => { console.log('on Event A', data); this.setState({myDataA: data}) }; handleEventB = (error) => { console.log('On Event B', error); }; } const mapStateToProps = (state) => { return { } } const mapDispatchToProps = (dispatch) => { return { } } export default connect(mapStateToProps, mapDispatchToProps)(CustomWrapperContainer) 

I followed from React Native , as well as several others, but so far I have not been fortunate enough to receive the event to transition from iOS to React Native . I also could not find significant help in this matter from existing articles.

Ignite uses react version 15.3.2 . Perhaps this question? Or is there a version of some other dependency there? I'm not sure. I would really appreciate any help or leads.

PS: I ran this on both devices and simulators with iOS 9.2 through 10.0 , and I see no change in behavior, so this is not a problem.

+5
source share
3 answers

Ok, so considering RCTBubblingEventBlock and RCTDirectEventBlock seem to be broken, I had to find another approach for passing callbacks to JS code. So I found that the approach used for Android with event emitters seems promising, and I found that iOS has an RCTEventEmitter object that I could use.

After much searching / browsing, I found this gist that helped me build my post from iOS to JS . It did not feel clean, and there was much more code to write to set it up, but it worked in the end. I hope so.

I also hope that the recommended method of using RCTBubblingEventBlock and RCTDirectEventBlock will work at some point!

+1
source

This answer may come late, but I hope this helps.

Firstly, thanks for your research. I also stuck with the same problem as yours, and then I had to use RCTEventEmitter as your answer.

But later I double-checked and looked at my code, I found a few points that help my code work with RCTBubblingEventBlock.

I saw that you are using

 MyCustomView.propTypes = { htmlInjection: React.PropTypes.string, onEventA: React.PropTypes.func, onEventB: React.PropTypes.func, 

and

 <CustomView {...this.props} onEventA={this._onEventA} onEventB={this._onEventB} /> 

In the {... this.props} block, all MyCustomView supports for CustomView will be installed. In another view, if onEventA and onEventB from MyCustomView are configured, then onEventA and onEventB CustomView will have the same value due to {... this.props}.

Try removing this in the define code of MyCustomView property

 onEventA: React.PropTypes.func, onEventB: React.PropTypes.func, 

Or you can use the config property for your own code only in the React-native MapView example

 const RCTMap = requireNativeComponent('RCTMap', MapView, { nativeOnly: { onAnnotationDragStateChange: true, onAnnotationFocus: true, onAnnotationBlur: true, onChange: true, onPress: true } }); 
+1
source

I had another problem with RCTBubblingEventBlock . All RCTBubblingEventBlock must have the on prefix. This is not currently documented.

For instance:

 onMyEvent //will work myEvent //no good 
+1
source

Source: https://habr.com/ru/post/1260703/


All Articles