React Native开发指南-在原生和React Native间通信

通过植入原生应用和原生UI组件两篇文档,我们学习了React Native和原生组件的互相整合。在整合的过程中,我们会需要在两个世界间互相通信。有些方法已经在其他的指南中提到了,这篇文章总结了所有可行的技术。

简介

React Native是从React中得到的灵感,因此基本的信息流是类似的。在React中信息是单向的。我们维护了组件层次,在其中每个组件都仅依赖于它父母和自己的状态。通过属性(properties)我们将信息从上而下的从父母传递到子元素。如果一个祖先组件需要自己子孙的状态,推荐的方法是传递一个回调函数给对应的子元素。

React Native也运用了相同的概念。只要我们完全在框架内构建应用,就可以通过属性和回调函数来调动整个应用。但是,当我们混合React Native和原生组件时,我们需要一些特殊的,跨语言的机制来传递信息。

属性

属性是最简单的跨组件通信。因此我们需要一个方法从原生组件传递属性到React Native或者从React Native到原生组件。

从原生组件传递属性到React Native

我们使用RCTRootView将React Natvie视图封装到原生组件中。RCTRootView是一个UIView容器,承载着React Native应用。同时它也提供了一个联通原生端和被托管端的接口。

通过RCTRootView的初始化函数你可以将任意属性传递给React Native应用。参数initialProperties必须是NSDictionary的一个实例。这一字典参数会在内部被转化为一个可供JS组件调用的JSON对象。

NSArray *imageList = @[@"http://foo.com/bar1.png",@"http://foo.com/bar2.png"];NSDictionary *props = @{@"images" : imageList};RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridgemoduleName:@"ImageBrowserApp"initialProperties:props];
'use strict';var React = require('react-native');var {View,Image
} = React;class ImageBrowserApp extends React.Component {renderImage: function(imgURI) {return (<Image source={{uri: imgURI}} />);},render() {return (<View>{this.props.images.map(this.renderImage)}</View>);}
}React.AppRegistry.registerComponent('ImageBrowserApp', () => ImageBrowserApp);

RCTRootView同样提供了一个可读写的属性appProperties。在appProperties设置之后,React Native应用将会根据新的属性重新渲染。当然,只有在新属性和之前的属性有区别时更新才会被触发。

NSArray *imageList = @[@"http://foo.com/bar3.png",@"http://foo.com/bar4.png"];
rootView.appProperties = @{@"images" : imageList};

你可以随时更新属性,但是更新必须在主线程中进行,读取则可以在任何线程中进行。
更新属性时并不能做到只更新一部分属性。我们建议你自己封装一个函数来构造属性。

注意:目前,最顶层的RN组件(即registerComponent方法中调用的那个)的componentWillReceivePropscomponentWillUpdateProps方法在属性更新后不会触发。但是,你可以通过componentWillMount访问新的属性值。

从React Native传递属性到原生组件

这篇文档详细讨论了暴露原生组件属性的问题。简而言之,在你自定义的原生组件中通过RCT_CUSTOM_VIEW_PROPERTY宏导出属性,就可以直接在React Native中使用,就好像它们是普通的React Native组件一样。

属性的限制

跨语言属性的主要缺点是不支持回调方法,因而无法实现自下而上的数据绑定。设想你有一个小的RN视图,当一个JS动作触发时你想从原生的父视图中移除它。此时你会发现根本做不到,因为信息需要自下而上进行传递。

虽然我们有跨语言回调(参阅这里,但是这些回调函数并不总能满足需求。最主要的问题是它们并不是被设计来当作属性进行传递。这一机制的本意是允许我们从JS触发一个原生动作,然后用JS处理那个动作的处理结果。

其他的跨语言交互(事件和原生模块)

如上一章所说,使用属性总会有一些限制。有时候属性并不足以满足应用逻辑,因此我们需要更灵活的解决办法。这一章描述了其他的在React Native中可用的通信方法。他们可以用来内部通信(在JS和RN的原生层之间),也可以用作外部通信(在RN和纯原生部分之间)。

React Native允许使用跨语言的函数调用。你可以在JS中调用原生代码,也可以在原生代码中调用JS。在不同端需要用不同的方法来实现相同的目的。在原生代码中我们使用事件机制来调度JS中的处理函数,而在React Native中我们直接使用原生模块导出的方法。

从原生代码调用React Natvie函数(事件)

事件的详细用法在这篇文章中进行了讨论。注意使用事件无法确保执行的时间,因为事件的处理函数是在单独的线程中执行。

事件很强大,它可以不需要引用直接修改React Native组件。但是,当你使用时要注意下面这些陷阱:

  • 由于事件可以从各种地方产生,它们可能导致混乱的依赖。
  • 事件共享相同的命名空间,因此你可能遇到名字冲突。冲突不会在编写代码时被探测到,因此很难排错。
  • 如果你使用了同一个React Native组件的多个引用,然后想在事件中区分它们,name你很可能需要在事件中同时传递一些标识(你可以使用原生视图中的reactTag作为标识)。

在React Native中嵌入原生组件时,通常的做法是用原生组件的RCTViewManager作为视图的代理,通过bridge向JS发送事件。这样可以集中在一处调用相关的事件。

从React Native中调用原生方法(原生模块)

原生模块是JS中也可以使用的Objective-C类。一般来说这样的每一个模块的实例都是在每一次通过JS bridge通信时创建的。他们可以导出任意的函数和常量给React Native。相关细节可以参阅这篇文章。

事实上原生模块的单实例模式限制了嵌入。假设我们有一个React Native组件被嵌入了一个原生视图,并且我们希望更新原生的父视图。使用原生模块机制,我们可以导出一个函数,不仅要接收预设参数,还要接收父视图的标识。这个标识将会用来获得父视图的引用以更新父视图。那样的话,我们需要维持模块中标识到原生模块的映射。 虽然这个解决办法很复杂,它仍被用在了管理所有React Native视图的RCTUIManager类中,

原生模块同样可以暴露已有的原生库给JS,地理定位库就是一个现成的例子。

警告:所有原生模块共享同一个命名空间。创建新模块时注意命名冲突。

布局计算流

当集成原生模块和React Natvie时,我们同样需要一个能协同不同的布局系统的办法。这一章节讨论了常见的布局问题,并且提供了解决机制的简单说明。

在React Native中嵌入一个原生组件

这个情况在这篇文章中进行了讨论。基本上,由于所有的原生视图都是UIView的子集,大多数类型和尺寸属性将和你期望的一样可以使用。

在原生中嵌入一个React Native组件

固定大小的React Native内容

最简单的情况是一个对于原生端已知的,固定大小的React Native应用,尤其是一个全屏的React Native视图。如果我们需要一个小一点的根视图,我们可以明确的设置RCTRootView的frame。 比如说,创建一个200像素高,宿主视图那样宽的RN app,我们可以这样做:

// SomeViewController.m- (void)viewDidLoad
{[...]RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridgemoduleName:appNameinitialProperties:props];rootView.frame = CGMakeRect(0, 0, self.view.width, 200);[self.view addSubview:rootView];
}

当我们创建了一个固定大小的根视图,则需要在JS中遵守它的边界。换句话说,我们需要确保React Native内容能够在固定的大小中放下。最简单的办法是使用flexbox布局。如果你使用绝对定位,并且React组件在根视图边界外可见,则React Native组件将会和原生视图重叠,导致某些不符合期望的行为。比如说,当你点击根视图边界之外的区域TouchableHighlight将不会高亮。 通过重新设置frame的属性来动态更新根视图的大小是完全可行的。React Native将会关注内容布局的变化。

弹性大小的React Native

有时候我们需要渲染一些不知道大小的内容。假设尺寸将会在JS中动态指定。我们有两个解决办法。

  • 你可以将React Native视图包裹在ScrollView中。这样可以保证你的内容总是可以访问,并且不会和原生视图重叠。
  • React Native允许你在JS中决定RN应用的尺寸,并且将它传递给宿主视图RCTRootView。然后宿主视图将重新布局子视图,保证UI统一。我们通过RCTRootView的弹性模式来达到目的。

RCTRootView支持4种不同的弹性模式:

// RCTRootView.htypedef NS_ENUM(NSInteger, RCTRootViewSizeFlexibility) {RCTRootViewSizeFlexibilityNone = 0,RCTRootViewSizeFlexibilityWidth,RCTRootViewSizeFlexibilityHeight,RCTRootViewSizeFlexibilityWidthAndHeight,
};

默认值是RCTRootViewSizeFlexibilityNone,表示使用固定大小的根视图(仍然可以通过setFrame更改)。其他三种模式可以跟踪React Native尺寸的变化。比如说,设置模式为RCTRootViewSizeFlexibilityHeight,React Native将会测量内容的高度然后传递回RCTRootView的代理。代理可以执行任意的行为,包括设置根视图的frame以使内容尺寸相匹配。 代理仅仅在内容的尺寸发生变化时才进行调用。

注意:在JS和原生中都设置弹性尺寸可能导致不确定的行为。比如--不要在设置RCTRootViewRCTRootViewSizeFlexibilityWidth时同时指定最顶层的RN组件宽度可变(使用Flexbox)。

看一个例子。

// FlexibleSizeExampleView.m- (instancetype)initWithFrame:(CGRect)frame
{[...]_rootView = [[RCTRootView alloc] initWithBridge:bridgemoduleName:@"FlexibilityExampleApp"initialProperties:@{}];_rootView.delegate = self;_rootView.sizeFlexibility = RCTRootViewSizeFlexibilityHeight;_rootView.frame = CGRectMake(0, 0, self.frame.size.width, 0);
}#pragma mark - RCTRootViewDelegate
- (void)rootViewDidChangeIntrinsicSize:(RCTRootView *)rootView
{CGRect newFrame = rootView.frame;newFrame.size = rootView.intrinsicSize;rootView.frame = newFrame;
}

在例子中我们使用一个FlexibleSizeExampleView视图来包含根视图。我们创建了根视图,初始化并且设置了代理。代理将会处理尺寸更新。然后,我们设置根视图的弹性尺寸为RCTRootViewSizeFlexibilityHeight,意味着rootViewDidChangeIntrinsicSize:方法将会在每次React Native内容高度变化时进行调用。最后,我们设置根视图的宽度和位置。注意我们也设置了高度,但是并没有效果,因为我们已经将高度设置为根据RN内容进行弹性变化了。

你可以在这里查看完整的例子源代码。

动态改变根视图的弹性模式是可行的。改变根视图的弹性模式将会导致布局的重新计算,并且在重新量出内容尺寸时会调用rootViewDidChangeIntrinsicSize方法。

注意:React Native布局是通过一个特殊的线程进行计算,而原生UI视图是通过主线程更新。这可能导致短暂的原生端和React Native端的不一致。这是一个已知的问题,我们的团队已经在着手解决不同源的UI同步更新。 注意:除非根视图成为其他视图的子视图,否则React Native不会进行任何的布局计算。如果你想在还没有获得React Native视图的尺寸之前先隐藏视图,请将根视图添加为子视图并且在初始化的时候进行隐藏(使用UIViewhidden属性),然后在代理方法中改变它的可见性。

本文转自React Native中文网:http://reactnative.cn/docs/0.20/communication-ios.html#content

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/500017.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据迁移记录

做数据迁移,数据库是分两片,每片一主两从,没有读写分离 前提: 使用服务器1:2核4G内存40G硬盘 线程池:5个 每个线程分页查询,每页1000条 平均数据是一天一个线程,一天3万数据,所以大概就是30页的分页深度 服务器1的数据: cpu使用率在74%上下(有点高,2核的毕竟比不过4核的…

RPC协议简介

一、概述 1.英文原义&#xff1a;Remote Procedure Call Protocol 2.中文释义&#xff1a;&#xff08;RFC-1831&#xff09;远程调用协议 。 3.注解&#xff1a;一种通过网络从远程计算机程序上请求服务&#xff0c;而不需要了解底层网络技术的协议。 4.说明&#xff1a;RPC…

mysql分页查询报错,及解决

mysql分页查询报错: 前提: 1.每页1000条数据 2.查到57页的时候,就报错了 以下是错误信息: org.springframework.jdbc.UncategorizedSQLException: ### Error querying database. Cause: java.sql.SQLException: ...省略中间... received message larger than max (1844105…

RPC协议与Web Service

一、引入 我们每天都在使用浏览器来上网冲浪, 在查找自己需要的资源, HTTP协议自然是我们使用的最多的 一种, 我们尽情地享受着这种信息高速路的快感,却没有试图去了解我们是如何获得这些资源的? 它是一种什么样的设计理念? 我们也偶尔会使用 Gtalk来和自己的同事或者朋友来聊…

如何设置电脑开机自动提示(防止忘打卡等)

如何设置电脑开机自动提示: 第一种方法: 第一步:CtrlR 输入mmc命令打开 文件-添加/删除管理单元-组策略对象编辑器-添加-确定-确定(都是默认的)。 第二步:退出小框,会看见有个”本地计算机 策略”,打开-计算机配置-windows设置-安全设置-本地策略-安全选项 双击”交互式登…

RestFull架构

1 什么是REST REST全称是Representational State Transfer&#xff0c;中文意思是表述性状态转移。 它首次出现在2000年Roy Fielding的博士论文中&#xff0c;Roy Fielding是 HTTP 规范的主要编写者之一。 他在论文中提到:“我这篇文章的写作目的&#xff0c;就是想在符合架构原…

SpringMVC默认访问路径配置

SpringMVC默认访问路径配置 需求:只访问域名,不加任何action路径,想访问默认的一个action. 比如www.jd.com,就跳到京东首页,会加载出辣么多东西来 关于这个的方法,网上有千千万种,在这里只是记录一种常用的, 亲测这个可以用,而且我们的项目一直都是这个套路. 第一步,web.x…

controller中执行main方法报错NoClassDefFoundError: javax/servlet/http/HttpServletResponse

controller中执行main方法报了这个错:NoClassDefFoundError: javax/servlet/http/HttpServletResponse,如下图: NoClassDefFoundError: javax/servlet/http/HttpServletResponse NoClassDefFoundError: javax/servlet/http/HttpServletRequest同理 原因是本地没有引入servlet…

初探Backbone

Backbone简介 中文API&#xff1a;http://www.css88.com/doc/backbone/ 英文API&#xff1a;http://backbonejs.org/ Backbone是构建javascript应用程序的一个优秀的类库。他简洁、轻量级、功能实在。 backbone采用MVC模式&#xff0c;本身提供了模型、控制器和视图从而我们应用…

订单量的监控

要解决的问题 : 1.在电商项目中,如何准确的知道当前订单量是正常的 2.如何在订单量突变后快速感知 解决思路 : 实现一个关于订单量的监控系统,将历史数据与实时数据做对比,因为每天的订单量,基本都是一个相似的变化范围,比如凌晨4点的单量是一天中最少的,节假日的单量要小于工…

JQuery初探

一、JQuery是什么&#xff1f; JQuery 是一套JavaScript库&#xff0c; 使用它&#xff0c;可以很方便的进行 JavaScript的编程。比如&#xff1a; 获取页面元素&#xff0c; 修改页面元素的CSS样式等等都可以以很简单的语法完成。节省代码行数和减少开发的时间。 物理上来看就…

BigDecimal的异常记录:java.lang.ArithmeticException: Rounding necessary

记一次报错,代码样例: public static void main(String[] args) {BigDecimal bigDecimal new BigDecimal(1234.5);int res bigDecimal.intValueExact();} 报错信息: Exception in thread "main" java.lang.ArithmeticException: Rounding necessaryat java.math.…

JavaScript历史与ECMAScript

一、前言为了发挥 JavaScript 的全部潜力&#xff0c;了解它的本质、历史及局限性是十分重要的。本节为您讲解 JavaScript 和客户端脚本的起源。二、Nombas 和 ScriptEase大概在 1992 年&#xff0c;一家称作 Nombas 的公司开发了一种叫做 C 减减&#xff08;C-minus-minus&…

基于abtest思想的流量切换(nginx lua redis)

使用前提: 项目重构了,旧项目还在线上运行,新项目准备替换线上的旧项目 最终目标: 要实现实时切换新旧项目,保证如果新项目上线后有问题,可以立刻快速的将流量切回旧项目 方案: 关于abtest的基本原理本文不再多说,本文重点是实践&#xff0c;先看图 如上图所示,用户访问的…

JavaScript内部实现

前言 JavaScript 的核心 ECMAScript 描述了该语言的语法和基本对象&#xff1b; DOM 描述了处理网页内容的方法和接口&#xff1b; BOM 描述了与浏览器进行交互的方法和接口。 ECMAScript、DOM 和 BOM 尽管 ECMAScript 是一个重要的标准&#xff0c;但它并不是 JavaScript 唯一…

jsonp跨域原理,使用以及同源策略

什么是跨域请求,为什么用跨域 浏览器从一个域名的网页去请求另一个域名的资源时&#xff0c;域名,端口,协议任何一个不同&#xff0c;都算是跨域请求 域名是一个网站的唯一标识,一个域名代表着一个网站以及其对应的服务 服务间调用可以使用基于soa思想的rpc调用,也可以是webs…

LESS语言简介

一、概述 LESS是一种 动态 样式 语言. LESS 将 CSS 赋予了动态语言的特性&#xff0c;如 变量&#xff0c; 继承&#xff0c; 运算&#xff0c; 函数. LESS 既可以在 客户端 上运行 (支持IE 6, Webkit, Firefox)&#xff0c;也可以借助Node.js或者Rhino在服务端运行。 LE…

非对称加密算法之RSA算法实现

对称加密&#xff0c;非对称加密&#xff0c;公钥&#xff0c;私钥&#xff0c;RSA这些常常听到的&#xff0c;到底是怎么回事 有个同事问我公钥私钥到底为什么能互相解开&#xff0c;一时语塞&#xff0c;平时都只是知道怎么用&#xff0c;但很少去了解的更细&#xff0c;现在…

流式计算storm核心组件介绍以及入门案例---跟着就能在本地跑起来的storm项目

关于storm的基础,参照我这篇文章:流式计算storm 关于并发和并行,参照我这篇文章:并发和并行 关于storm的并行度解释,参照我这篇文章:storm的并行度解释 关于storm的流分组策略,参照我这篇文章:storm的流分组策略 关于storm的消息可靠机制,参照我这篇文章:storm的消息可靠机制 …

javascript中的this讲解

查看this指向的一句话法则&#xff1a;永远指向其所在函数的所有者如果没有所有者时&#xff0c;指向window。理解this的要点&#xff1a;关键在于将函数与函数名分开看待。同一个函数&#xff0c;在不同的执行方法下&#xff0c;会有不同的效果。如何来进行理解呢&#xff0c;…