【问题标题】:React Native UI components: RCTBubblingEventBlock/RCTDirectEventBlock do not seem to workReact Native UI 组件:RCTBubblingEventBlock/RCTDirectEventBlock 似乎不起作用
【发布时间】:2017-04-17 07:21:21
【问题描述】:

我在 Ignite 项目中有一个自定义原生视图。我正在尝试建立从Objective-CReact Native 的通信。从React NativeiOS 的通信与HTML 注入一起工作,但反之则不行。我尝试过同时使用RCTBubblingEventBlockRCTDirectEventBlock,但都不起作用。这是我的全部实现。当然,我已经更改了组件的名称,只留下了基本的实现,以便您了解到目前为止所做的工作:

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 本身以及其他几个关注example,但到目前为止我还没有运气将事件从iOS 传递到React Native。我也无法从现有文章中找到这方面的重要帮助。

Ignite 使用 react 版本 15.3.2。也许这就是问题所在?或者那里的其他依赖项的版本?我不知道。非常感谢任何帮助或线索。

P.S.:我一直在运行 iOS 9.210.0 的设备和模拟器上运行它,我没有看到行为有任何变化,所以这不是问题。

【问题讨论】:

  • 看到这个我首先想到的是,一定要考虑尝试 RNWebview 桥的 v2。这将很快登陆 RN Master:github.com/alinz/react-native-webview-bridge/tree/v2
  • 谢谢。但我不明白这与我遇到的问题有什么关系。我确实有一个 webView,我可以在其中加载 HTML 内容,并且工作正常。只有当我尝试通过回调传回数据时,通信才不起作用。
  • 是的,抱歉,我没有发现这段代码有任何问题,所以我希望能提供一些额外的信息。你在我可以访问的仓库中有这个代码吗?
  • 啊,恐怕代码有访问限制。但我计划在纯反应本机项目中运行相同的代码,以确认这不是一件容易的事。关于我可以做些什么来调试这个,你有什么其他的理论或建议吗?
  • @GantMan 我已经发布了一个答案并且该方法有效。有cmets吗?

标签: javascript ios objective-c reactjs react-native


【解决方案1】:

我遇到了 RCTBubblingEventBlock 的另一个问题。所有RCTBubblingEventBlock 必须以on 为前缀。文档中目前没有说明这一点。

例如:

onMyEvent //will work
myEvent   //no good

【讨论】:

  • 谢谢@Ulises,你拯救了我的一天。 :)
【解决方案2】:

这个答案可能来晚了,但我希望它会有所帮助。

首先感谢您的研究。我也遇到了和你一样的问题,然后我不得不使用 RCTEventEmitter 作为你的答案。

但后来我仔细检查并审查了我的代码,我发现了一些有助于我的代码在 RCTBubblingEventBlock 上运行良好的点。

我看到你在使用

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 的所有 props 设置为 CustomView。在另一个视图中,如果配置了 MyCustomView 的 onEventA 和 onEventB,则 CustomView 的 onEventA 和 onEventB 将具有相同的值,因为 {...this.props}。

尝试在 MyCustomView 的属性定义代码中删除它

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

或者您可以在 React-native MapView 示例中将 config 属性用于仅本地代码

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

【讨论】:

  • 谢谢@khanhha!该解决方案似乎很有希望。我希望你能在一个月前来,这真的很有帮助。但我相信这个解决方案将来也会对我有用。 :)
【解决方案3】:

好吧,鉴于RCTBubblingEventBlockRCTDirectEventBlock 似乎已损坏,我必须找到另一种将回调传递给JS 代码的方法。所以我发现Android 采用事件发射器的方法看起来很有希望,我确实发现iOS 有一个我可以使用的RCTEventEmitter 对象。

在玩了很多/环顾四周之后,我发现了这个gist,它帮助我建立了从iOSJS 的通信。它感觉不干净,并且要编写更多代码来设置它,但它最终还是奏效了。我希望它保持这种状态。

我也希望使用RCTBubblingEventBlockRCTDirectEventBlock 的推荐方法在某个时候有效!

【讨论】:

  • 哇!感谢您研究和发现这一点!我不知道。
猜你喜欢
  • 1970-01-01
  • 2021-05-11
  • 1970-01-01
  • 1970-01-01
  • 2016-12-26
  • 1970-01-01
  • 2014-12-03
  • 2019-11-21
  • 1970-01-01
相关资源
最近更新 更多