非常教程

React native参考手册

指南(IOS) | Guides (iOS)

Communication between native and React Native

需要本机代码的项目

此页面仅适用于react-native init使用Create React Native App 制作的或使用此类应用程序弹出的项目。有关弹出的更多信息,请参阅创建React Native App存储库的指南。

在与现有应用程序指南和本地用户界面组件指南集成中,我们将学习如何将本机组件嵌入React Native,反之亦然。当我们混合native和React Native组件时,我们最终会发现需要在这两个世界之间进行通信。其他指南中已经提到了一些实现这些方法的方法。本文总结了可用技术。

介绍

React Native受React的启发,所以信息流的基本思想是相似的。React中的流程是单向的。我们维护一个组件的层次结构,其中每个组件只依赖于它的父节点和它自己的内部状态。我们用属性来做到这一点:数据以自顶向下的方式从父项传递给子项。如果祖先组件依赖于它的后代的状态,那么应该传递一个回调供后裔用来更新祖先。

相同的概念适用于React Native。只要我们纯粹在框架内构建应用程序,就可以通过属性和回调来驱动我们的应用程序。但是,当我们将React Native与本地组件混合在一起时,我们需要一些特殊的跨语言机制,允许我们在它们之间传递信息。

属性

属性是交叉组件通信的最简单方式。所以我们需要一种方法来将属性从本地传递给React Native,并从React Native传递给本地。

将属性从本机传递给React Native

为了在本地组件中嵌入React Native视图,我们使用RCTRootViewRCTRootView是一个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:bridge
                                                 moduleName:@"ImageBrowserApp"
                                          initialProperties:props];
'use strict';

import React from 'react';
import {
  AppRegistry,
  View,
  Image
} from 'react-native';

class ImageBrowserApp extends React.Component {
  renderImage(imgURI) {
    return (
      <Image source={{uri: imgURI}} />
    );
  }
  render() {
    return (
      <View>
        {this.props.images.map(this.renderImage)}
      </View>
    );
  }
}

AppRegistry.registerComponent('AwesomeProject', () => ImageBrowserApp);

RCTRootView还提供了一个读写属性appProperties。之后appProperties被设置,阵营本地应用程序进行重新渲染与新的属性。仅当新的更新属性与先前的属性不同时才执行更新。

NSArray *imageList = @[@"http://foo.com/bar3.png",
                       @"http://foo.com/bar4.png"];

rootView.appProperties = @{@"images" : imageList};

随时更新属性是很好的。但是,更新必须在主线程上执行。你在任何线程上使用getter。

没有办法一次只更新一些属性。我们建议您将其构建到自己的包装中。

注意:目前,在更新道具后,JS功能componentWillReceivePropscomponentWillUpdateProps顶级RN组件不会被调用。但是,您可以在componentWillMount功能中访问新的道具。

将属性从React Native传递给本地

本文详细介绍了本机组件的问题暴露问题。简而言之,RCT_CUSTOM_VIEW_PROPERTY在您的自定义本地组件中使用宏导出属性,然后在React Native中使用它们,就好像组件是普通的React Native组件。

属性的限制

跨语言属性的主要缺点是它们不支持回调,这将允许我们处理自下而上的数据绑定。想象一下,您有一个小的RN视图,您想从JS动作的结果中将其从本地父视图中删除。道具无法做到这一点,因为信息需要自下而上。

尽管我们有跨语言回调的风格(这里描述),但这些回调并不总是我们需要的东西。主要问题是它们不打算作为属性传递。相反,这种机制允许我们从JS中触发本地操作,并处理JS中该操作的结果。

跨语言交互的其他方式(事件和本机模块)

如前一章所述,使用属性会带来一些限制。有时,属性不足以驱动我们的应用程序的逻辑,我们需要一个提供更多灵活性的解决方案。本章介绍React Native中可用的其他通信技术。它们可以用于内部通信(在RN和本地层之间)以及外部通信(在RN和应用程序的“纯本地”部分之间)。

React Native允许您执行跨语言函数调用。你可以从JS执行自定义的本地代码,反之亦然。不幸的是,取决于我们正在努力的方面,我们以不同的方式实现相同的目标。对于本机 - 我们使用事件机制来安排JS中处理函数的执行,而对于React Native,我们直接调用由本地模块导出的方法。

从本机(事件)调用React Native函数

本文将详细介绍事件。请注意,使用事件不能保证执行时间,因为事件在单独的线程上处理。

事件功能强大,因为它们允许我们更改React Native组件,而无需引用它们。但是,在使用它们时会遇到一些陷阱:

  • 由于事件可以从任何地方发送,他们可以将意大利面风格的依赖项引入到您的项目中。
  • 事件共享名称空间,这意味着您可能会遇到一些名称冲突。碰撞不会被静态检测到,这使得它们很难调试。
  • 如果您使用相同React Native组件的多个实例,并且希望将它们与事件的角度区分开来,则可能需要引入标识符并将它们与事件一起传递(可以使用本地视图reactTag作为标识符)。

我们在将本机嵌入到React Native中时使用的常见模式是让本地组件的RCTViewManager成为视图的委托,并通过桥将事件发送回JavaScript。这将相关的事件调用保存在一个地方。

从React Native调用本地函数(本机模块)

原生模块是JS中可用的Objective-C类。通常每个JS桥都创建一个模块的一个实例。他们可以将任意函数和常量导出到React Native。这篇文章详细介绍了它们。

本地模块是单例的事实限制了嵌入上下文中的机制。假设我们在本地视图中嵌入了React Native组件,并且我们想更新本地父视图。使用本机模块机制,我们将导出一个函数,该函数不仅需要预期的参数,而且还包含父本机视图的标识符。该标识符将用于检索对父视图的引用以进行更新。也就是说,我们需要在模块中保留从标识符到本地视图的映射。

尽管此解决方案非常复杂,但它用于RCTUIManager管理所有React Native视图的内部React Native类。

本地模块也可用于将现有的本地库展示给JS。该地理位置图书馆的理念是一个活生生的例子。

警告:所有本地模块共享相同的名称空间。注意创建新名称时的名称冲突。

布局计算流程

在集成本机和React Native时,我们还需要一种方法来整合两种不同的布局系统。本节涵盖常见的布局问题,并提供解决这些问题的机制的简要说明。

嵌入在React Native中的本地组件的布局

本文将介绍这种情况。基本上,由于我们所有的原生反应视图都是子类UIView,因此大多数样式和大小属性都可以像您期望的那样工作。

嵌入在本机中的React Native组件的布局

以固定大小回应原生内容

最简单的情况是当我们有一个固定大小的React Native应用程序时,它是本机端已知的。特别是,全屏React Native视图属于这种情况。如果我们想要一个更小的根视图,我们可以明确地设置RCTRootView的框架。

例如,要制作一个RN应用程序200(逻辑)像素高,并且托管视图的宽度很宽,我们可以这样做:

// SomeViewController.m

- (void)viewDidLoad
{
  [...]
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:appName
                                            initialProperties:props];
  rootView.frame = CGRectMake(0, 0, self.view.width, 200);
  [self.view addSubview:rootView];
}

当我们有一个固定大小的根视图时,我们需要尊重它在JS方面的界限。换句话说,我们需要确保React Native内容可以包含在固定大小的根视图中。确保这一点的最简单方法是使用flexbox布局。如果使用绝对定位,并且React组件在根视图的边界外可见,则会与本机视图重叠,导致某些功能出现意外情况。例如,'TouchableHighlight'不会在根视图的界限之外突出显示您的触摸。

通过重新设置其帧属性动态更新根视图的大小是完全正确的。React Native会处理内容的布局。

以灵活的大小回应原生内容

在某些情况下,我们希望呈现最初未知大小的内容。比方说,大小将在JS中动态定义。我们有两个解决这个问题的方法。

  • 你可以将你的React Native视图包装在一个ScrollView组件中。这可以确保您的内容始终可用,并且不会与本机视图重叠。
  • React Native允许您在JS中确定RN应用程序的大小并将其提供给托管所有者RCTRootView。然后,所有者负责重新布局子视图并保持UI一致。我们RCTRootView通过灵活性模式实现这一目标。

RCTRootView 支持4种不同尺寸的灵活模式:

// RCTRootView.h

typedef NS_ENUM(NSInteger, RCTRootViewSizeFlexibility) {
  RCTRootViewSizeFlexibilityNone = 0,
  RCTRootViewSizeFlexibilityWidth,
  RCTRootViewSizeFlexibilityHeight,
  RCTRootViewSizeFlexibilityWidthAndHeight,
};

RCTRootViewSizeFlexibilityNone是默认值,这使得固定根视图的大小(但它仍然可以更新setFrame:)。其他三种模式允许我们跟踪React Native内容的大小更新。例如,将模式设置为RCTRootViewSizeFlexibilityHeight将导致React Native测量内容的高度并将该信息传递给RCTRootView代理人。可以在委托中执行任意操作,包括设置根视图的框架,以便内容符合要求。只有在内容大小发生变化时才会调用委托。

警告:在JS和本机中使维度灵活导致未定义的行为。例如,flexbox当您在RCTRootViewSizeFlexibilityWidth主机上使用时,请勿使顶层React组件的宽度变得灵活(带)RCTRootView

我们来看一个例子。

// FlexibleSizeExampleView.m

- (instancetype)initWithFrame:(CGRect)frame
{
  [...]

  _rootView = [[RCTRootView alloc] initWithBridge:bridge
  moduleName:@"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.intrinsicContentSize;

  rootView.frame = newFrame;
}

在这个例子中,我们有一个FlexibleSizeExampleView持有根视图的视图。我们创建根视图,初始化它并设置委托。代表将处理大小更新。然后,我们将根视图的大小灵活性设置为RCTRootViewSizeFlexibilityHeight,这意味着rootViewDidChangeIntrinsicSize:每次React Native内容更改高度时都会调用该方法。最后,我们设置根视图的宽度和位置。请注意,我们也设置了高度,但它没有效果,因为我们取决于高度RN。

您可以在这里查看示例的完整源代码。

动态改变根视图的大小灵活模式是很好的。更改根视图的灵活模式将计划布局重新计算,rootViewDidChangeIntrinsicSize:一旦内容大小已知,将调用委托方法。

注意: React本机布局计算是在特殊线程上执行的,而本机UI视图更新是在主线程上完成的。这可能会导致本机和React Native之间的临时UI不一致。这是一个已知的问题,我们的团队正在努力同步来自不同来源的UI更新。 注意: React Native不会执行任何布局计算,直到根视图成为其他某些视图的子视图。如果你想隐藏阵营本地视图,直到它的尺寸是已知的,加根视图作为一个子视图,并使其最初是隐藏的(使用UIViewhidden属性)。然后在委托方法中更改其可见性。

React native

React Native 是一个 JavaScript 的框架,用来撰写实时的、可原生呈现 iOS 和 Android 的应用。

主页 https://facebook.github.io/react-native/
源码 https://github.com/facebook/react-native
发布版本 0.49

React native目录

1.开始 | Getting Started
2.指南 | Guides
3.APIs
4.组件:ActivityIndicator | Components: ActivityIndicator
5.组件:按钮 | Components: Button
6.组件:CheckBox | Components: CheckBox
7.组件:DatePickerIOS | Components: DatePickerIOS
8.组件:DrawerLayoutAndroid | Components: DrawerLayoutAndroid
9.组件:FlatList | Components: FlatList
10.组件:图像 | Components: Image
11.组件:KeyboardAvoidingView | Components: KeyboardAvoidingView
12.Components: ListView
13.Components: MaskedViewIOS
14.Components: Modal
15.Components: NavigatorIOS
16.Components: Picker
17.Components: PickerIOS
18.Components: ProgressBarAndroid
19.Components: ProgressViewIOS
20.Components: RefreshControl
21.Components: ScrollView
22.Components: SectionList
23.Components: SegmentedControlIOS
24.Components: Slider
25.Components: SnapshotViewIOS
26.Components: StatusBar
27.Components: Switch
28.Components: TabBarIOS
29.Components: TabBarIOS.Item
30.Components: Text
31.Components: TextInput
32.Components: ToolbarAndroid
33.Components: TouchableHighlight
34.Components: TouchableNativeFeedback
35.Components: TouchableOpacity
36.Components: TouchableWithoutFeedback
37.Components: View
38.Components: ViewPagerAndroid
39.Components: VirtualizedList
40.Components: WebView
41.创建 | Contributing
42.指南(Android) | Guides (Android)
43.指南(IOS) | Guides (iOS)
44.其他 | Miscellaneous