2016-12-02 35 views
5

私はIgniteプロジェクト内にカスタムネイティブビューを持っています。 Objective-CからReact Nativeへの通信を設定しようとしています。 React NativeからiOSへの通信は、HTML注射で動作しますが、逆の動作はしません。私はRCTBubblingEventBlockRCTDirectEventBlockの両方を使用しようとしましたが、いずれも動作しません。ここに私の実装の全体があります。私はもちろんのコンポーネントの名前を変更し、ちょうどこれまで行われてきた内容をご理解のために不可欠な実装を残した:ネイティブUIコンポーネントに反応する:RCTBubblingEventBlock/RCTDirectEventBlockが機能しないようです

Objective-Cコード:

// 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 NativeJavaScriptコード:

// 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) 

私はをReact Nativeとそれ以外のものに従っていますが、これまでのところイベントを得るのに運がなかったのでiOSReact Native。既存の記事からこの問題の重要な助けを得ることもできませんでした。

Igniteは、reactバージョン15.3.2を使用します。多分それは問題ですか?または、他の依存関係のバージョン?よく分かりません。私はどんな助けやリードにも大いに感謝しています。

P .:私はiOS 9.2から10.0を実行しているデバイスとシミュレータの両方でこれを実行していましたが、動作に変化は見られないため、問題はありません。

+0

私はこれを見たときに考える最初の事は、RNWebviewブリッジのV2をしようと検討していることを確認してくださいです。すぐにRN Masterに着陸します:https://github.com/alinz/react-native-webview-bridge/tree/v2 – GantMan

+0

ありがとうございます。しかし、私はこれが私が持つ問題にどのように関連しているのか理解していません。私はWebViewを持っているので、HTMLコンテンツを読み込むとうまくいきます。コールバックを使って通信がうまくいかないというデータを返すときだけです。 –

+0

ええ、申し訳ありませんが、私はこのコードに間違ったことは何も見られなかったので、いくつかの追加情報を提供したいと考えていました。私がアクセス可能なレポにこのコードがありますか? – GantMan

答えて

1

いいえ、RCTBubblingEventBlockRCTDirectEventBlockが壊れているように見えるので、私はJSコードにコールバックを渡す別の方法を見つけなければなりませんでした。だから、私は、Androidのイベントエミッタとの取り組みが有望に見えて、iOSにはRCTEventEmitterオブジェクトがあることがわかりました。

多くの再生/見回しの後、iOSからJSへの通信を構築するのに役立つこのgistが見つかりました。それはきれいに感じられませんでした。そして、それをセットアップするために書くべきもっと多くのコードがありましたが、それは最後に働きました。私はそれがそのようにとどまることを望む。

また、RCTBubblingEventBlockRCTDirectEventBlockをお勧めします。

+1

うわー!これを研究して発見してくれてありがとう!私は今まで知らなかった。 – GantMan

1

この回答は遅くなるかもしれませんが、それが役に立ちそうです。

まずはあなたの研究に感謝します。私もあなたと同じ問題に悩まされ、あなたの答えとしてRCTEventEmitterを使わなければなりませんでした。

しかし、後でコードを再確認してレビューしたところ、コードがRCTBubblingEventBlockで正常に動作するのに役立ついくつかの点が見つかりました。

は、私はあなたがのCustomViewにMyCustomViewのすべての小道具を設定します

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

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

ブロック{...} this.propsを使用しているのを見ました。別のビューでは、MyCustomViewのonEventAとonEventBが設定されていると、{... this.props}のためにCustomViewのonEventAとonEventBが同じ値になります。プロパティでこれを除去するための

てみMyCustomView

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

のコードを定義したり、私は別のに走った

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

ありがとう@khanhha!その解決策は有望だと思われる。私はあなたが1ヶ月前に来るといいと思いますが、本当に役に立ちました。しかし、私はこのソリューションが将来私にとっても役立つだろうと確信しています。 :) –

0

リアクトネイティブのMapViewの例ではネイティブコードのみのための設定プロパティを使用することができますRCTBubblingEventBlockとの問題。 RCTBubblingEventBlockには、すべて接頭辞onを付ける必要があります。これは現在、ドキュメントの下には記載されていません。例えば

onMyEvent //will work 
myEvent //no good 
関連する問題