跳到主要内容

2019 年 3 月 React Native 开源更新

·6 分钟阅读
Christoph Nakazawa
Christoph Nakazawa
Facebook 前工程师

在决定加大对 React Native 开源社区的投入后,我们于 2018 年第四季度宣布了我们的 React Native 开源路线图

对于第一个里程碑,我们专注于识别和改进社区最显著的方面。我们的目标是减少未处理的拉取请求,缩小项目的表面积,识别主要的用户问题,并建立社区管理准则。

在过去的两个月里,我们取得了比预期更大的进展。请继续阅读以获取更多详情

拉取请求

为了建立一个健康的社区,我们必须迅速响应代码贡献。过去几年,我们对社区贡献的审查优先级较低,累积了 280 个拉取请求(2018 年 12 月)。在第一个里程碑中,我们将开放的拉取请求数量减少到约 65 个。同时,每日开放的拉取请求平均数量从 3.5 个增加到 7 个,这意味着我们在过去三个月中处理了大约600 个拉取请求

我们合并了近三分之二的拉取请求,并关闭了三分之一。如果它们过时、质量低下或不必要地增加了项目的表面积,则在未合并的情况下关闭。大多数合并的拉取请求修复了错误、改善了跨平台兼容性或引入了新功能。值得注意的贡献包括改进类型安全和正在进行的 AndroidX 支持工作。

在 Facebook,我们从主分支运行 React Native,因此我们在所有更改进入 React Native 发布版本之前都会先进行测试。在所有合并的拉取请求中,只有六个导致了问题:四个只影响内部开发,两个在发布候选阶段被发现。

社区贡献中一个更引人注目的例子是更新后的“RedBox”屏幕。这是一个很好的例子,说明社区如何使开发者体验更加友好。

精简核心

React Native 目前拥有非常广泛的表面区域,其中许多未维护的抽象我们并未在 Facebook 大量使用。我们正在努力缩小表面区域,以使 React Native 更小,并允许社区更好地维护在 Facebook 很少使用的抽象。

在第一个里程碑中,我们请求社区在精简核心项目上提供帮助。反响非常热烈,我们几乎跟不上所有的进展。查看在不到一个月内完成的所有工作

最令我们兴奋的是,维护者们纷纷介入,修复了长期存在的问题,添加了测试,并支持了长期以来要求的功能。这些模块获得了比以往在 React Native 中更多的支持,这表明这是社区迈出的一大步。此类项目的示例包括 WebView,自其提取以来已收到许多拉取请求;以及 CLI,现在由社区成员维护,并获得了急需的改进和修复。

主要用户问题

去年十二月,我们询问社区他们对 React Native 有何不满。我们汇总了回复并对每一个问题进行了回复。幸运的是,我们社区面临的许多问题在 Facebook 内部也存在。在下一个里程碑中,我们计划解决其中一些主要问题。

投票最多的问题之一是升级到新版本 React Native 的开发者体验。不幸的是,这不是我们自己会遇到的问题,因为我们是从 master 分支运行 React Native 的。幸运的是,社区成员已经主动解决了这个问题

0.59 发布

如果没有 React Native 社区的帮助,特别是Mike GrabowskiLorenzo Sciandra,我们将无法发布版本。我们希望改进发布管理流程,并计划从现在开始更多地参与其中

  • 我们将与社区成员合作,为每个主要版本撰写博客文章。
  • 当用户升级到新版本时,我们将直接在 CLI 中显示重大更改。
  • 我们将缩短发布所需的时间。我们正在探索增加自动化测试的方法,并创建改进的手动测试计划。

许多这些计划将纳入即将发布的React Native 0.59 版本。0.59 将附带 React Hooks、Android 新的 64 位 JavaScriptCore 以及许多性能和功能改进。它目前已作为发布候选版本发布,预计将在未来两周内稳定。

后续步骤

在接下来的两个月里,我们将继续管理拉取请求,以保持进度,同时开始减少未解决的 GitHub 问题数量。我们将通过精简核心项目继续缩小 React Native 的表面积。我们计划解决社区面临的五大问题。在最终确定社区准则后,我们将把注意力转向我们的网站和文档。

我们很高兴三月在 Facebook 伦敦接待十多位社区贡献者,以帮助推动其中几项工作。我们很高兴您正在使用 React Native,并希望您能看到并感受到我们 2019 年正在进行的改进。我们将在几个月后再次更新,同时 将合并您的拉取请求! ⚛️✌️

2018 年 React Native 社区现状

·4 分钟阅读
Lorenzo Sciandra
核心维护者 & React Native 开发者

在 2018 年,React Native 社区对我们开发和交流 React Native 的方式进行了一些改变。我们相信几年后回顾时,我们会发现这次转变是 React Native 的一个转折点。

许多人对 React Native 架构的重写感到兴奋,这通常被称为Fabric。除了其他功能之外,这将修复 React Native 架构中的根本限制,并将与JSI 和 TurboModules一起为 React Native 未来的成功奠定基础。

2018 年最大的转变是赋予 React Native 社区更大的权力。从一开始,Facebook 就鼓励来自世界各地的开发者参与 React Native 的开源项目。从那时起,一些核心贡献者应运而生,主要负责发布流程。

这些成员采取了一些实质性措施,通过以下资源赋能整个社区来塑造这个项目的未来

react-native-releases 📬

这个在今年一月创建的仓库,其双重目的在于允许所有人以更协作的方式跟踪新版本发布,并为那些希望建议 cherry-pick(例如 0.57.8 及其所有早期版本)的人开放了关于特定版本内容的话题。

这成为了摆脱每月发布周期,并采用目前用于 0.57.x 版本的“长期支持”方法背后的驱动力。

做出这些决定的一半功劳归功于今年创建的另一个存储库

discussions-and-proposals 🗣

这个仓库于七月创建,扩展了为 React Native 讨论提供更开放环境的理念。此前,这种需求通过主仓库中标记为For Discussion的问题来处理,但我们希望将此策略扩展到其他库(如 React)采用的 RFC 方法。

这项实验立即在 React Native 的生命周期中找到了其作用。Facebook 团队现在正在使用社区 RFC 流程来讨论React Native 中可以改进的地方,并协调围绕精简核心项目的工作——以及其他有趣的讨论。

@ReactNativeComm 🐣

我们意识到,我们传达这些努力的方法并未达到预期的效果,为了让大家更容易了解 React Native 社区(从发布到活跃讨论)正在发生的一切,我们创建了一个新的 Twitter 帐户,您可以关注它:@ReactNativeComm

如果您不在该社交网络上,请记住您始终可以通过 GitHub 监视仓库;此功能在过去几个月中得到了改进,现在可以选择仅接收发布通知,因此您无论如何都应该考虑使用它。

未来展望 🎓

在过去的 7-8 个月里,核心贡献者们增强了React Native 社区 GitHub 组织,使其在 React Native 的开发中拥有更多所有权,并加强与 Facebook 的协作。但是,这始终缺乏类似项目可能具备的正式结构。

该组织可以通过对其托管的所有包/仓库强制执行一套标准,为维护者提供一个互相帮助和贡献符合社区约定标准的优质代码的单一场所,从而为更广泛的开发者社区树立榜样。

2019 年初,我们将推出这套新的指导方针。请在专门的讨论中告诉我们您的想法。

我们相信,通过这些改变,社区将变得更具协作性,这样当我们达到 1.0 版本时,我们都将继续利用这种共同努力来编写(更)出色的应用 🤗


我希望您和我们一样对这个社区的未来感到兴奋。我们很高兴看到您参与到上述仓库中正在进行的讨论中,或者通过您将产生的出色代码来参与。

编码愉快!

开源路线图

·5 分钟阅读
Héctor Ramos
Facebook 工程师

今年,React Native 团队专注于对 React Native 进行大规模的重新架构。正如 Sophie 在她的React Native 2018 现状文章中提到的,我们制定了一个计划,以更好地支持 Facebook 之外日益壮大的 React Native 用户和贡献者群体。现在是时候分享更多关于我们一直在努力的细节了。在此之前,我想阐述一下我们对 React Native 在开源领域的长期愿景。

我们对 React Native 的愿景是……

  • 一个健康的 GitHub 仓库。 问题和拉取请求能在合理的时间内得到处理。
    • 增加测试覆盖率。
    • 从 Facebook 代码仓库同步出来的提交不应破坏开源测试。
    • 更高规模的有意义的社区贡献。
  • 稳定的 API, 使其更容易与开源依赖项进行接口对接。
    • Facebook 使用与开源相同的公共 API。
    • 遵循语义化版本控制的 React Native 发布。
  • 一个充满活力的生态系统。 由社区维护的高质量 ViewManager、原生模块和多平台支持。
  • 出色的文档。 专注于帮助用户创建高质量的体验,以及最新的 API 参考文档。

我们已确定以下重点领域,以帮助我们实现这一愿景。

✂️ 精简核心

我们的目标是通过移除非核心和未使用的组件来减小 React Native 的表面积。我们将把非核心组件移交给社区,以使其能更快发展。表面积的减小将使管理 React Native 的贡献变得更容易。

WebView是一个我们已移交给社区的组件示例。我们正在研究一种工作流程,允许内部团队在我们将这些组件从仓库中移除后继续使用它们。我们已经确定了数十个更多将所有权移交给社区的组件。

🎁 开源内部组件和 🛠更新工具

Facebook 内部产品团队的 React Native 开发体验与开源社区可能大不相同。在开源社区中流行的工具可能在 Facebook 不被使用。可能存在一个内部工具可以实现相同的目的。在某些情况下,Facebook 团队已经习惯了在 Facebook 外部不存在的工具。这些差异可能在我们将即将到来的架构工作开源时带来挑战。

我们将致力于发布一些内部工具。我们还将改进对开源社区流行工具的支持。以下是我们将要处理的部分项目列表(并非详尽无遗)

  • 开源 JSI 并使社区能够引入自己的 JavaScript VM,取代 RN 初始版本中现有的 JavaScriptCore。我们将在后续文章中介绍 JSI 是什么,在此期间您可以从Parashuram 在 React Conf 上的演讲中了解更多关于 JSI 的信息。
  • 支持 Android 上的 64 位库。
  • 在新架构下启用调试。
  • 改进对 CocoaPods、Gradle、Maven 和新 Xcode 构建系统的支持。

✅ 测试基础设施

当 Facebook 工程师发布代码时,如果通过所有测试,则认为可以安全发布。这些测试用于识别更改是否可能破坏我们自己的 React Native 界面。然而,Facebook 使用 React Native 的方式存在差异。这使得我们可能会在不知不觉中破坏开源 React Native。

我们将加强内部测试,确保它们在尽可能接近开源的环境中运行。这将有助于防止破坏这些测试的代码进入开源。我们还将致力于基础设施建设,以实现对 GitHub 上的核心仓库进行更好的测试,从而使未来的拉取请求能够轻松包含测试。

结合缩小的表面积,这将使贡献者能够更快、更自信地合并拉取请求。

📜 公共 API

Facebook 将像开源一样通过公共 API 使用 React Native,以减少意外的重大变更。我们已经开始转换内部调用站点来解决这个问题。我们的目标是收敛于一个稳定的公共 API,从而在 1.0 版本中采用语义化版本控制。

📣 沟通

按贡献者数量计算,React Native 是 GitHub 上最顶尖的开源项目之一。这让我们非常高兴,我们希望继续保持下去。我们将继续致力于促进贡献者参与的举措,例如提高透明度和开放讨论。文档是 React Native 新手接触到的第一批内容之一,但它一直没有得到优先重视。我们希望解决这个问题,首先是恢复自动生成的 API 参考文档,创建更多专注于创建高质量用户体验的内容,并改进我们的发布说明

时间线

我们计划在未来一年左右完成这些项目。其中一些工作已经展开,例如JSI 已经进入开源领域。其他工作将需要更长的时间才能完成,例如缩小表面积。我们将尽最大努力让社区了解我们的进展。请加入我们位于讨论和提案仓库,这是 React Native 社区的一项倡议,已经促成了本路线图中讨论的几项倡议的创建。

介绍新的 iOS WebViews

·3 分钟阅读
Facebook 软件工程师

长期以来,Apple 一直不鼓励使用 UIWebView,转而推荐 WKWebView。在未来几个月将发布的 iOS 12 中,UIWebView 将被正式弃用。React Native 的 iOS WebView 实现严重依赖 UIWebView 类。因此,鉴于这些发展,我们为 WebView React Native 组件构建了一个使用 WKWebView 的新原生 iOS 后端。

这些更改的最后部分已在此提交中落地,并将随 0.57 版本发布。

要启用此新实现,请使用useWebKit 属性

<WebView
useWebKit={true}
source={{url: 'https://www.google.com'}}
/>

改进

UIWebView 没有合法的方式来促进 WebView 中运行的 JavaScript 与 React Native 之间的通信。当从 WebView 发送消息时,我们依赖一个 hack 来将它们传递给 React Native。简而言之,我们通过一个特殊方案将消息数据编码到 URL 中,并将 WebView 导航到该 URL。在原生端,我们拦截并取消了此导航,从 URL 中解析了数据,最后调用了 React Native。这种实现容易出错且不安全。我很高兴地宣布,我们利用 WKWebView 的功能完全取代了它。

WKWebView 相对于 UIWebView 的其他优点包括更快的 JavaScript 执行和多进程架构。有关更多详细信息,请参阅此2014 年 WWDC

注意事项

如果您的组件使用以下属性,则在切换到 WKWebView 时可能会遇到问题。目前,我们建议您避免使用这些属性

行为不一致

automaticallyAdjustContentInsetscontentInsets (提交)

当您向 WKWebView 添加内容边距时,它不会改变 WKWebView 的视口。视口大小与帧大小相同。而使用 UIWebView,视口大小实际上会改变(如果内容边距为正,则会变小)。

backgroundColor (提交)

在 WebView 的新 iOS 实现中,如果您使用此属性,您的背景颜色可能会闪烁进入视图。此外,WKWebView 处理透明背景的方式与 UIWebview 不同。请查看提交描述以获取更多详细信息。

不支持

scalesPageToFit (提交)

WKWebView 不支持 scalesPageToFit 属性,因此我们无法在 WebView React Native 组件上实现此功能。

无障碍 API 更新

·7 分钟阅读
Ziqi Chen
加州大学伯克利分校学生

动机

随着科技的进步和移动应用在日常生活中变得越来越重要,创建可访问应用程序的必要性也随之增加。

React Native 有限的无障碍 API 一直是开发人员的一个巨大痛点,因此我们对无障碍 API 进行了几次更新,以使其更容易创建包容性移动应用程序。

现有 API 的问题

问题一:两个完全不同但相似的属性 - accessibilityComponentType (Android) 和 accessibilityTraits (iOS)

accessibilityComponentTypeaccessibilityTraits 是两个属性,用于告知 Android 上的 TalkBack 和 iOS 上的 VoiceOver 用户正在与哪种 UI 元素交互。这两个属性最大的问题是

  1. 它们是两个具有不同使用方法但目的相同的属性。 在以前的 API 中,它们是两个独立的属性(每个平台一个),这不仅不方便,而且让许多开发人员感到困惑。iOS 上的 accessibilityTraits 允许 17 种不同的值,而 Android 上的 accessibilityComponentType 只允许 4 种值。此外,这些值在大多数情况下没有重叠。甚至这两个属性的输入类型也不同。accessibilityTraits 允许传入特性数组或单个特性,而 accessibilityComponentType 只允许单个值。
  2. Android 上的功能非常有限。 使用旧属性时,Talkback 能够识别的 UI 元素只有“button”、“radiobutton_checked”和“radiobutton_unchecked”。

问题二:不存在的无障碍提示:

辅助功能提示可帮助使用 TalkBack 或 VoiceOver 的用户了解在辅助功能元素上执行操作时会发生什么,这些信息仅凭辅助功能标签并不明显。这些提示可以在设置面板中开启和关闭。以前,React Native 的 API 完全不支持辅助功能提示。

问题三:忽略反色:

一些视力受损的用户会在手机上使用反色以获得更高的屏幕对比度。Apple 为 iOS 提供了一个 API,允许开发者忽略某些视图。这样,当用户开启反色设置时,图像和视频就不会失真。此 API 目前不受 React Native 支持。

新 API 的设计

解决方案一:合并 accessibilityComponentType (Android) 和 accessibilityTraits (iOS)

为了解决 accessibilityComponentTypeaccessibilityTraits 之间的混淆,我们决定将它们合并为一个属性。这很有意义,因为它们在技术上具有相同的预期功能,并且通过合并它们,开发人员在构建无障碍功能时不再需要担心平台特定的复杂性。

背景

在 iOS 上,UIAccessibilityTraits 是一个可以设置在任何 NSObject 上的属性。通过 javascript 属性传递给原生端的 17 个特性中的每一个都映射到 Objective-C 中的 UIAccessibilityTraits 元素。特性都由一个长整型表示,并且设置的每个特性都进行位或运算。

然而,在 Android 上,AccessibilityComponentType 是一个由 React Native 创建的概念,它不直接映射到 Android 中的任何属性。辅助功能由一个辅助功能委托处理。每个视图都有一个默认的辅助功能委托。如果您想自定义任何辅助功能操作,您必须创建一个新的辅助功能委托,覆盖您想要自定义的特定方法,然后将您正在处理的视图的辅助功能委托设置为与新的委托相关联。当开发人员设置 AccessibilityComponentType 时,原生代码会根据传入的组件创建一个新的委托,并将视图设置为具有该辅助功能委托。

所做更改

对于我们的新属性,我们希望创建这两个属性的超集。我们决定将新属性主要建模为现有属性 accessibilityTraits,因为 accessibilityTraits 具有明显更多的值。这些特性在 Android 上的功能将通过修改 Accessibility Delegate 来实现。

iOS 上 accessibilityTraits 可以设置 17 个 UIAccessibilityTraits 值。但是,我们并没有将所有这些值都作为新属性的可能值。这是因为设置其中一些特性效果并不为人熟知,而且许多这些值实际上几乎从未使用过。

UIAccessibilityTraits 的设置值通常具有两种目的之一。它们要么描述 UI 元素所扮演的角色,要么描述 UI 元素所处的状态。我们观察到以前属性的大多数用法通常使用一个表示角色的值,并将其与“状态选中”、“状态禁用”或两者结合使用。因此,我们决定创建两个新的可访问性属性:accessibilityRoleaccessibilityState

accessibilityRole

新属性 accessibilityRole 用于告知 Talkback 或 Voiceover UI 元素的角色。此新属性可以采用以下值之一

  • 按钮
  • 链接
  • 搜索
  • 图片
  • 键盘键
  • 文本
  • 可调节
  • 标题
  • 摘要
  • 图片按钮

此属性只允许传入一个值,因为 UI 元素通常在逻辑上不会同时拥有多个这些角色。图像和按钮是例外,因此我们添加了一个角色 `imagebutton`,它是两者的组合。

accessibilityStates

新属性 accessibilityStates 用于告知 Talkback 或 Voiceover UI 元素所处的状态。此属性接受一个数组,其中包含以下一个或两个值

  • 选中
  • 禁用

解决方案二:添加辅助功能提示

为此,我们添加了一个新属性 accessibilityHint。设置此属性将允许 Talkback 或 Voiceover 向用户朗读提示。

accessibilityHint

此属性以字符串形式接收要读取的辅助功能提示。

在 iOS 上,设置此属性将设置视图上相应的原生属性 AccessibilityHint。如果 iPhone 中开启了辅助功能提示,则 Voiceover 将朗读该提示。

在 Android 上,设置此属性会将提示的值附加到辅助功能标签的末尾。这种实现的好处是它模拟了 iOS 上提示的行为,但缺点是这些提示无法像在 iOS 上那样在 Android 的设置中关闭。

我们做出此决定的原因在于,通常情况下,辅助功能提示与特定操作(例如点击)相关联,我们希望保持跨平台行为的一致性。

问题三的解决方案

accessibilityIgnoresInvertColors

我们已将 Apple 的 API AccessibilityIgnoresInvertColors 暴露给 JavaScript,因此现在当您有一个不希望颜色反转的视图(例如图像)时,您可以将此属性设置为 true,它就不会被反转。

新用法

这些新属性将在 React Native 0.57 版本中可用。

如何升级

如果您当前正在使用 accessibilityComponentTypeaccessibilityTraits,这里是您可以采取的升级到新属性的步骤。

1. 使用 jscodeshift

最简单的用例可以通过运行 jscodeshift 脚本来替换。

脚本替换以下实例

accessibilityTraits=“trait”
accessibilityTraits={[“trait”]}

accessibilityRole= “trait”

此脚本还会删除 AccessibilityComponentType 的实例(假设您在设置 AccessibilityComponentType 的任何地方也会设置 AccessibilityTraits)。

2. 使用手动代码转换

对于使用 AccessibilityTraits 但没有对应 AccessibilityRole 值的情况,以及将多个特性传递给 AccessibilityTraits 的情况,则需要进行手动代码转换。

总的来说,

accessibilityTraits= {[“button”, “selected”]}

将被手动替换为

accessibilityRole=“button”
accessibilityStates={[“selected”]}

这些属性已经在 Facebook 的代码库中使用。Facebook 的代码转换出奇地简单。jscodeshift 脚本修复了大约一半的实例,另一半则手动修复。总的来说,整个过程不到几个小时。

希望您会发现更新后的 API 有用!请继续使应用程序可访问!#包容性

发布 0.56

·6 分钟阅读
Lorenzo Sciandra
Drivetribe 核心维护者 & React Native 开发者

期待已久的 React Native 0.56 版本现已发布 🎉。这篇博客文章重点介绍了此新版本中引入的一些更改。我们还想借此机会解释自三月以来让我们一直忙碌的原因。

重大更改困境,或者说,“何时发布?”

贡献者指南解释了所有 React Native 更改所经历的集成过程。该项目由许多不同的工具组成,需要协调和持续支持才能使一切正常运行。再加上为项目贡献的活跃开源社区,您将感受到这一切令人难以置信的规模。

随着 React Native 的广泛采用,必须非常谨慎地进行重大更改,并且过程不如我们所愿那样顺利。我们决定跳过四月和五月的发布,以允许核心团队集成和测试一套新的重大更改。专门的社区沟通渠道在此过程中被用于确保 2018 年 6 月(0.56.0)版本对于耐心等待稳定版本的人来说尽可能无忧。

0.56.0 完美吗?不,就像所有软件一样:但我们已经达到了一个平衡点,在“等待更多稳定性”和“测试成功,可以向前推进”之间,我们觉得已经准备好发布了。此外,我们意识到在最终的 0.56.0 版本中仍有一些问题尚未解决。大多数开发人员升级到 0.56.0 应该不会有任何问题。对于那些被上述问题阻碍的开发人员,我们希望在我们的讨论中见到您,并期待与您一起解决这些问题。

您可能会将 0.56.0 视为一个更稳定框架的基础构建模块:它可能需要一两周的广泛采用才能解决所有边缘情况,但这将带来一个更好的 2018 年 7 月(0.57.0)版本。

我们想以感谢所有 67 位贡献者对总计 818 次提交的辛勤工作来结束本节内容,这些工作将帮助您的应用程序变得更好 👏。

现在,言归正传……

重大变更

Babel 7

正如您所知,允许我们所有人使用 JavaScript 最新和最强大功能的转译工具 Babel 即将升级到v7 版本。由于这个新版本带来了一些重要的变化,我们觉得现在是升级的好时机,这将使Metro能够利用其改进

如果您在升级过程中遇到问题,请参阅相关文档部分

Android 支持的现代化

在 Android 上,许多配套工具都发生了变化。我们已更新到Gradle 3.5Android SDK 26Fresco 1.9.0 和 OkHttp 3.10.0,甚至还将NDK API 目标设置为 API 16。这些更改应该会顺利进行,并带来更快的构建速度。更重要的是,它将帮助开发人员遵守下个月生效的新的 Play 商店要求

与此相关,我们特别感谢Dulmandakh提交了许多 PR,使其成为可能👏。

在这方面还需要采取更多步骤,您可以在相关问题(以及JSC的另一个问题)中跟踪 Android 支持更新的未来规划和讨论。

新版 Node、Xcode、React 和 Flow – 哦,我的天呐!

Node 8 现在是 React Native 的标准。它实际上已经进行了测试,但随着 Node 6 进入维护模式,我们全力以赴。React 也已更新到 16.4,带来了大量的修复。

我们正在放弃对 iOS 8 的支持,将 iOS 9 定为可支持的最旧 iOS 版本。我们预计这不会成为问题,因为任何可以运行 iOS 8 的设备都可以升级到 iOS 9。这一更改使我们能够删除为运行 iOS 8 的旧设备实现变通方法的很少使用的代码。

持续集成工具链已更新以使用 Xcode 9.4,确保所有 iOS 测试都在 Apple 提供的最新开发者工具上运行。

我们已升级到Flow 0.75 以使用许多开发者赞赏的新错误格式。我们还为更多组件创建了类型。如果您尚未在项目中强制执行静态类型检查,请考虑使用 Flow 在编码时而不是运行时识别问题。

还有很多其他事情……

例如,YellowBox 被新实现取代,这大大改善了调试体验。

有关完整的发布说明,请参阅此处的完整更新日志。并请记住关注升级指南,以避免迁移到此新版本时出现问题。


最后一点:从本周开始,React Native 核心团队将恢复每月例会。我们将确保向所有人及时通报会议内容,并确保将您的反馈牢记在心,以供未来会议参考。

祝大家编码愉快!

LorenzoRyan以及整个React Native 核心团队

附注: 和往常一样,我们想提醒大家,React Native 仍处于 0.x 版本,因为还有许多更改正在进行中——所以请记住,在升级时,是的,可能仍然会有一些东西崩溃或损坏。在问题中和提交 PR 时互相帮助——并记住遵守强制执行的行为准则:屏幕的另一端始终是人。

2018 年 React Native 现状

·5 分钟阅读
Sophie Alpert
Facebook React 团队工程经理

我们已经有一段时间没有发布关于 React Native 的状态更新了。

在 Facebook,我们比以往任何时候都更频繁地使用 React Native,并将其用于许多重要项目。我们最受欢迎的产品之一是 Marketplace,它是我们应用中顶级标签之一,每月有 8 亿人使用。自 2015 年创建以来,Marketplace 的所有部分都使用 React Native 构建,包括应用不同部分中超过一百个全屏视图。

我们还在应用程序的许多新部分中使用 React Native。如果您上个月观看了 F8 主题演讲,您会认出“献血”、“危机响应”、“隐私快捷方式”和“健康检查”——这些都是最近用 React Native 构建的功能。主 Facebook 应用程序之外的项目也在使用 React Native。新的 Oculus Go VR 头显包含一个完全用 React Native 构建的配套移动应用程序,更不用说 React VR 为头显中的许多体验提供支持。

当然,我们也使用许多其他技术来构建我们的应用程序。LithoComponentKit 是我们应用程序中广泛使用的两个库;两者都为构建原生屏幕提供了类似 React 的组件 API。React Native 的目标从来不是取代所有其他技术——我们专注于使 React Native 本身变得更好,但我们很高兴看到其他团队借鉴 React Native 的思想,例如将即时热重载引入非 JavaScript 代码。

架构

2013 年我们启动 React Native 项目时,我们将其设计为一个 JavaScript 和原生代码之间唯一的“桥接”,它是异步、可序列化且批处理的。正如 React DOM 将 React 状态更新转换为对 DOM API(如 document.createElement(attrs).appendChild())的命令式、可变调用一样,React Native 被设计为返回一个列出要执行的突变的单个 JSON 消息,例如 [["createView", attrs], ["manageChildren", ...]]。我们设计整个系统,使其永远不依赖于获取同步响应,并确保列表中的所有内容都可以完全序列化为 JSON 并返回。我们这样做是为了它带给我们的灵活性:在这种架构之上,我们能够构建诸如Chrome 调试之类的工具,它通过 WebSocket 连接异步运行所有 JavaScript 代码。

在过去的 5 年中,我们发现这些初始原则使构建某些功能变得更加困难。异步桥接意味着您无法将 JavaScript 逻辑直接与许多需要同步响应的原生 API 集成。批量处理原生调用意味着 React Native 应用程序更难以调用原生实现的函数。而可序列化桥接意味着不必要的复制,而不是直接在两个世界之间共享内存。对于完全使用 React Native 构建的应用程序,这些限制通常是可以忍受的。但对于 React Native 和现有应用程序代码之间复杂集成的应用程序,它们令人沮丧。

我们正在对 React Native 进行大规模的重新架构,以使框架更加灵活,并更好地与混合 JavaScript/原生应用程序中的原生基础设施集成。 通过这个项目,我们将应用过去 5 年所学到的知识,并逐步将我们的架构现代化。我们正在重写许多 React Native 的内部结构,但大多数更改都在幕后:现有 React Native 应用程序将继续运行,几乎或根本不需要更改。

为了使 React Native 更轻量级并更好地适应现有原生应用,此次重新架构有三个主要的内部变化。首先,我们正在改变线程模型。与其让每次 UI 更新都必须在三个不同的线程上执行工作,现在可以针对高优先级更新在任何线程上同步调用 JavaScript,同时仍将低优先级工作排除在主线程之外以保持响应速度。其次,我们正在将异步渲染能力整合到 React Native 中,以允许多个渲染优先级并简化异步数据处理。最后,我们正在简化我们的桥接,使其更快更轻量级;原生和 JavaScript 之间的直接调用效率更高,并将使构建跨语言堆栈跟踪等调试工具变得更容易。

一旦这些更改完成,将有可能实现更紧密的集成。如今,如果不使用复杂的技巧,就无法整合原生导航和手势处理,或像 UICollectionView 和 RecyclerView 这样的原生组件。在我们的线程模型更改后,构建此类功能将变得简单明了。

随着这项工作的临近完成,我们将在今年晚些时候发布更多细节。

社区

除了 Facebook 内部的社区,我们也很高兴在 Facebook 之外拥有一个蓬勃发展的 React Native 用户和合作者群体。我们希望更多地支持 React Native 社区,既要更好地服务 React Native 用户,又要使项目更容易贡献。

正如我们的架构变更将帮助 React Native 与其他原生基础设施更清晰地互操作一样,React Native 在 JavaScript 方面也应该更精简,以更好地适应 JavaScript 生态系统,这包括使 VM 和打包器可互换。我们知道重大变更的节奏可能难以跟上,因此我们希望找到减少主要版本发布数量的方法。最后,我们知道有些团队正在寻找更全面的文档,例如启动优化等主题,我们的专业知识尚未被记录下来。预计在未来一年内会看到其中一些变化。

如果您正在使用 React Native,您就是我们社区的一员;请继续让我们知道我们如何能为您做得更好。

React Native 只是移动开发人员工具箱中的一个工具,但我们坚信它——并且我们每天都在改进它,在过去一年中,来自 500 多位贡献者提交了 2500 多个提交。

将 TypeScript 与 React Native 结合使用

·8 分钟阅读
Ash Furrow
Artsy 软件工程师

JavaScript!我们都爱它。但我们中的一些人也喜欢类型。幸运的是,存在为 JavaScript 添加强类型的方法。我最喜欢的是TypeScript,但 React Native 原生支持Flow。您喜欢哪一个取决于个人偏好,它们都有各自的方法来为 JavaScript 添加类型魔法。今天,我们将探讨如何在 React Native 应用程序中使用 TypeScript。

这篇文章以 Microsoft 的TypeScript-React-Native-Starter仓库作为指南。

更新:自这篇博客文章撰写以来,事情变得更加简单了。您可以通过运行一个命令来替换这篇博客文章中描述的所有设置

npx react-native init MyAwesomeProject --template react-native-template-typescript

然而,Babel 的 TypeScript 支持确实存在一些限制,上述博客文章对此进行了详细阐述。此文章中概述的步骤仍然有效,并且 Artsy 仍在生产环境中使用react-native-typescript-transformer,但是最快上手 React Native 和 TypeScript 的方法是使用上述命令。如果需要,您随时可以切换。

无论如何,玩得开心!原博客文章内容继续如下。

先决条件

由于您可能在多个不同的平台上进行开发,并针对多种不同类型的设备,因此基本设置可能比较复杂。您应该首先确保您可以在不使用 TypeScript 的情况下运行一个普通的 React Native 应用程序。请按照React Native 网站上的说明开始。当您成功部署到设备或模拟器后,就可以开始一个 TypeScript React Native 应用程序了。

您还需要Node.jsnpmYarn

初始化

一旦您尝试搭建了一个普通的 React Native 项目,您就可以开始添加 TypeScript 了。让我们继续操作。

react-native init MyAwesomeProject
cd MyAwesomeProject

添加 TypeScript

下一步是将 TypeScript 添加到您的项目中。以下命令将

  • 将 TypeScript 添加到您的项目
  • React Native TypeScript Transformer 添加到您的项目
  • 初始化一个空的 TypeScript 配置文件,我们接下来将配置它
  • 添加一个空的 React Native TypeScript Transformer 配置文件,我们接下来将配置它
  • 添加 React 和 React Native 的类型定义

好的,让我们继续运行这些命令。

yarn add --dev typescript
yarn add --dev react-native-typescript-transformer
yarn tsc --init --pretty --jsx react
touch rn-cli.config.js
yarn add --dev @types/react @types/react-native

tsconfig.json 文件包含 TypeScript 编译器的所有设置。上面命令创建的默认设置大部分都很好,但请打开文件并取消注释以下行

{
/* Search the config file for the following line and uncomment it. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
}

rn-cli.config.js 包含 React Native TypeScript Transformer 的设置。打开它并添加以下内容

module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
},
};

迁移到 TypeScript

将生成的 App.js__tests_/App.js 文件重命名为 App.tsxindex.js 需要使用 .js 扩展名。所有新文件都应使用 .tsx 扩展名(如果文件不包含任何 JSX,则使用 .ts)。

如果您现在尝试运行该应用程序,您会收到类似“对象原型可能只是一个对象或空”的错误。这是由于无法在同一行同时导入 React 的默认导出和命名导出所致。打开 App.tsx 并修改文件顶部的导入

-import React, { Component } from 'react';
+import React from 'react'
+import { Component } from 'react';

这其中一些与 Babel 和 TypeScript 在 CommonJS 模块互操作性方面的差异有关。未来,两者将在相同行为上趋于稳定。

至此,您应该能够运行 React Native 应用程序了。

添加 TypeScript 测试基础设施

React Native 内置了Jest,因此为了使用 TypeScript 测试 React Native 应用,我们需要将ts-jest添加到我们的 devDependencies 中。

yarn add --dev ts-jest

然后,我们将打开 package.json 并将 jest 字段替换为以下内容

{
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
],
"cacheDirectory": ".jest/cache"
}
}

这将配置 Jest 以使用 ts-jest 运行 .ts.tsx 文件。

安装依赖类型声明

为了在 TypeScript 中获得最佳体验,我们希望类型检查器能够理解我们依赖项的形状和 API。一些库将通过 .d.ts 文件(类型声明/类型定义文件)发布其包,这些文件可以描述底层 JavaScript 的形状。对于其他库,我们需要在 @types/ npm 作用域中明确安装相应的包。

例如,这里我们需要 Jest、React、React Native 和 React Test Renderer 的类型。

yarn add --dev @types/jest @types/react @types/react-native @types/react-test-renderer

我们将这些声明文件包保存到我们的 开发 依赖项中,因为这是一个 React Native 应用程序,它只在开发过程中使用这些依赖项,而不在运行时使用。如果我们要将库发布到 NPM,我们可能需要将其中一些类型依赖项添加为常规依赖项。

您可以在此处了解更多关于获取 .d.ts 文件的信息。

忽略更多文件

对于您的源代码管理,您会希望开始忽略 .jest 文件夹。如果您正在使用 git,我们可以直接将条目添加到我们的 .gitignore 文件中。

# Jest
#
.jest/

作为一个检查点,请考虑将您的文件提交到版本控制。

git init
git add .gitignore # import to do this first, to ignore our files
git add .
git commit -am "Initial commit."

添加组件

让我们向应用程序添加一个组件。我们来创建一个 Hello.tsx 组件。这是一个教学组件,不是您在应用程序中实际会编写的组件,但它是一个非平凡的组件,可以展示如何在 React Native 中使用 TypeScript。

创建 components 目录并添加以下示例。

// components/Hello.tsx
import React from 'react';
import {Button, StyleSheet, Text, View} from 'react-native';

export interface Props {
name: string;
enthusiasmLevel?: number;
}

interface State {
enthusiasmLevel: number;
}

export class Hello extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

if ((props.enthusiasmLevel || 0) <= 0) {
throw new Error(
'You could be a little more enthusiastic. :D',
);
}

this.state = {
enthusiasmLevel: props.enthusiasmLevel || 1,
};
}

onIncrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel + 1,
});
onDecrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel - 1,
});
getExclamationMarks = (numChars: number) =>
Array(numChars + 1).join('!');

render() {
return (
<View style={styles.root}>
<Text style={styles.greeting}>
Hello{' '}
{this.props.name +
this.getExclamationMarks(this.state.enthusiasmLevel)}
</Text>

<View style={styles.buttons}>
<View style={styles.button}>
<Button
title="-"
onPress={this.onDecrement}
accessibilityLabel="decrement"
color="red"
/>
</View>

<View style={styles.button}>
<Button
title="+"
onPress={this.onIncrement}
accessibilityLabel="increment"
color="blue"
/>
</View>
</View>
</View>
);
}
}

// styles
const styles = StyleSheet.create({
root: {
alignItems: 'center',
alignSelf: 'center',
},
buttons: {
flexDirection: 'row',
minHeight: 70,
alignItems: 'stretch',
alignSelf: 'center',
borderWidth: 5,
},
button: {
flex: 1,
paddingVertical: 0,
},
greeting: {
color: '#999',
fontWeight: 'bold',
},
});

哇!内容很多,但我们来分解一下

  • 我们不是渲染 divspanh1 等 HTML 元素,而是渲染 ViewButton 等组件。这些是可以在不同平台上运行的原生组件。
  • 样式是使用 React Native 提供给我们的 StyleSheet.create 函数指定的。React 的样式表允许我们使用 Flexbox 控制布局,并使用与 CSS 类似的构造进行样式设计。

添加组件测试

既然我们有了一个组件,现在来测试它。

我们已经安装了 Jest 作为测试运行器。我们将为我们的组件编写快照测试,让我们添加快照测试所需的附加组件

yarn add --dev react-addons-test-utils

现在让我们在 components 目录下创建一个 __tests__ 文件夹,并为 Hello.tsx 添加一个测试

// components/__tests__/Hello.tsx
import React from 'react';
import renderer from 'react-test-renderer';

import {Hello} from '../Hello';

it('renders correctly with defaults', () => {
const button = renderer
.create(<Hello name="World" enthusiasmLevel={1} />)
.toJSON();
expect(button).toMatchSnapshot();
});

首次运行测试时,它将创建渲染组件的快照并将其存储在 components/__tests__/__snapshots__/Hello.tsx.snap 文件中。当您修改组件时,您需要更新快照并检查更新以防止无意中的更改。您可以在此处了解更多关于测试 React Native 组件的信息。

后续步骤

查看官方的React 教程和状态管理库Redux。这些资源在编写 React Native 应用时会很有帮助。此外,您可能还会想看看ReactXP,这是一个完全用 TypeScript 编写的组件库,支持 Web 上的 React 和 React Native。

在更类型安全的 React Native 开发环境中玩得开心!

使用 React Native 构建 - Build.com 应用

·6 分钟阅读
Garrett McCullough
高级移动工程师

Build.com 总部位于加利福尼亚州奇科,是最大的家居装修用品在线零售商之一。该团队拥有 18 年强大的以网络为中心的业务,并于 2015 年开始考虑开发移动应用程序。由于我们团队规模较小且原生经验有限,因此构建独特的 Android 和 iOS 应用程序并不实际。相反,我们决定冒险尝试非常新的 React Native 框架。我们的首次提交是在 2015 年 8 月 12 日,使用了 React Native v0.8.0!我们于 2016 年 10 月 15 日在两个应用商店上线。在过去的两年中,我们不断升级和扩展该应用程序。我们目前使用的是 React Native 0.53.0 版本。

您可以在 https://www.build.com/app 了解该应用。

功能特性

我们的应用功能齐全,包含了您对一个电商应用的所有期望:产品列表、搜索和排序、配置复杂产品的功能、收藏夹等。我们接受标准信用卡支付方式,以及PayPal,并为我们的iOS用户提供Apple Pay支付。

一些您可能意想不到的亮点功能包括

  1. 约40种产品提供3D模型,共90种表面处理选项
  2. 增强现实(AR)功能允许用户以98%的尺寸精度查看灯具和水龙头在他们家中的效果。Build.com React Native 应用在Apple App Store中被推荐为AR购物应用!AR现在可在Android和iOS上使用!
  3. 协作项目管理功能,允许人们为项目不同阶段创建购物清单,并就选择进行协作

我们正在开发许多令人兴奋的新功能,将继续提升我们的应用体验,包括AR沉浸式购物的下一阶段。

我们的开发流程

Build.com允许每位开发人员选择最适合自己的工具。

  • 集成开发环境(IDE)包括 Atom、IntelliJ、VS Code、Sublime、Eclipse 等。
  • 在单元测试方面,开发人员负责为任何新组件创建Jest单元测试,我们正致力于使用jest-coverage-ratchet提高应用旧部分的覆盖率。
  • 我们使用Jenkins构建我们的Beta版和发布候选版本。这个过程对我们来说很顺利,但仍需要大量工作来创建发布说明和其他工件。
  • 集成测试包括一个共享测试人员池,他们跨桌面、移动和Web平台工作。我们的自动化工程师正在使用Java和Appium构建我们的自动化集成测试套件。
  • 工作流程的其他部分包括详细的eslint配置、强制执行测试所需属性的自定义规则,以及阻止不当更改的pre-push钩子。

应用中使用的库

Build.com应用依赖于许多常见的开源库,包括:Redux、Moment、Numeral、Enzyme以及一系列React Native桥接模块。我们还使用了一些派生(forked)的开源库;派生原因要么是它们已被废弃,要么是我们需要自定义功能。粗略统计显示大约有115个JavaScript和原生依赖项。我们希望探索能够移除未使用的库的工具。

我们正在通过TypeScript添加静态类型,并研究可选链。这些功能可以帮助我们解决仍遇到的几类错误

  • 数据类型错误
  • 数据未定义,因为对象不包含我们期望的内容

开源贡献

由于我们高度依赖开源,我们的团队致力于回馈社区。Build.com允许团队开源我们自己构建的库,并鼓励我们回馈我们使用的库。

我们已发布并维护了多个React Native库

  • react-native-polyfill
  • react-native-simple-store
  • react-native-contact-picker

我们还为众多库做出了贡献,包括:React和React Native, react-native-schemes-manager, react-native-swipeable, react-native-gallery, react-native-view-transformer, react-native-navigation

我们的历程

在过去几年中,我们目睹了React Native及其生态系统的巨大发展。早期,似乎每个React Native版本都会修复一些错误,但同时引入更多新问题。例如,Android上的远程JS调试曾中断数月。值得庆幸的是,2017年情况变得更加稳定。

我们反复遇到的一大挑战是导航库。很长一段时间以来,我们一直使用Expo的ex-nav库。它对我们来说运作良好,但最终被弃用。然而,当时我们正处于大量功能开发阶段,所以没有时间更换导航库。这意味着我们不得不派生(fork)该库并打补丁以支持React 16和iPhone X。最终,我们成功迁移到react-native-navigation,希望它能继续得到支持。

桥接模块

另一个主要挑战是桥接模块。当我们刚开始时,很多关键的桥接模块缺失。我的一位队友编写了react-native-contact-picker,因为我们需要在应用中访问Android联系人选择器。我们也看到许多桥接模块因React Native内部的更改而损坏。例如,React Native v40中有一个破坏性更改,当我们升级应用时,我不得不提交PR来修复3或4个尚未更新的库。

展望未来

随着React Native的持续发展,我们对社区的期望包括

  • 稳定并改进导航库
  • 维护对React Native生态系统中库的支持
  • 改善向项目添加原生库和桥接模块的体验

React Native社区中的公司和个人都非常乐意贡献他们的时间和精力来改进我们都在使用的工具。如果您还没有参与开源,我希望您能尝试改进您使用的一些库的代码或文档。有很多文章可以帮助您入门,而且可能比您想象的要容易得多!

为 React Native 构建 <InputAccessoryView>

·7 分钟阅读
Peter Argany
Facebook 软件工程师

动机

三年前,GitHub上曾有一个议题,旨在支持React Native的输入辅助视图。

在随后的几年里,关于这个问题有无数的'+1'、各种权宜之计,但RN方面没有实质性的改变——直到今天。从iOS开始,我们正在开放一个API来访问原生输入辅助视图,我们很高兴能分享我们是如何构建它的。

背景

输入辅助视图到底是什么?阅读Apple的开发者文档,我们了解到它是一个自定义视图,当接收者成为第一响应者时,可以将其锚定到系统键盘的顶部。任何继承自UIResponder的对象都可以将.inputAccessoryView属性重新声明为读写,并在此处管理一个自定义视图。响应者基础设施会挂载该视图,并使其与系统键盘保持同步。解散键盘的手势,如拖动或点击,会在框架层面应用于输入辅助视图。这使我们能够构建带有交互式键盘解散功能的内容,这是iMessage和WhatsApp等顶级消息应用不可或缺的特性。

将视图锚定到键盘顶部有两种常见用例。第一种是创建键盘工具栏,例如Facebook撰写器背景选择器。

在这种情况下,键盘聚焦于文本输入字段,输入辅助视图用于提供额外的键盘功能。此功能与输入字段的类型相关。在地图应用中,它可能是地址建议;在文本编辑器中,它可能是富文本格式工具。


在这种情况下,拥有<InputAccessoryView>的Objective-C UIResponder应该很清楚。<TextInput>已成为第一响应者,其底层将成为UITextViewUITextField的一个实例。

第二种常见场景是粘性文本输入

这里,文本输入实际上是输入辅助视图本身的一部分。这常用于消息应用中,用户可以在滚动浏览之前的消息线程时撰写消息。


在这个例子中,谁拥有<InputAccessoryView>?它还能是UITextViewUITextField吗?文本输入位于输入辅助视图内部,这听起来像是一个循环依赖。仅解决这个问题本身就可以写另一篇博客文章了。剧透:所有者是一个通用的UIView子类,我们手动告诉它成为第一响应者

API设计

我们现在知道什么是<InputAccessoryView>以及我们希望如何使用它。下一步是设计一个对这两种用例都适用,并能与现有React Native组件(如<TextInput>)良好协作的API。

对于键盘工具栏,我们需要考虑以下几点

  1. 我们希望能够将任何通用的React Native视图层级提升到<InputAccessoryView>中。
  2. 我们希望这个通用且分离的视图层级能够接受触摸并能够操作应用状态。
  3. 我们希望将一个<InputAccessoryView>链接到一个特定的<TextInput>
  4. 我们希望能够在多个文本输入之间共享一个<InputAccessoryView>,而无需复制任何代码。

我们可以使用类似于React portals的概念实现第一点。在这个设计中,我们将React Native视图导入到由响应者基础设施管理的UIView层级结构中。由于React Native视图渲染为UIViews,这实际上非常直接——我们只需覆盖

- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex

并将所有子视图导入到新的UIView层级结构中。对于第二点,我们为<InputAccessoryView>设置了一个新的RCTTouchHandler。状态更新通过常规事件回调实现。对于第三点和第四点,我们在创建<TextInput>组件时,使用nativeID字段在原生代码中定位辅助视图UIView层级结构。此函数使用底层原生文本输入的.inputAccessoryView属性。这样做有效地将<InputAccessoryView><TextInput>在其ObjC实现中链接起来。

支持粘性文本输入(场景2)增加了一些额外的限制。对于这种设计,输入辅助视图有一个文本输入作为子级,因此通过nativeID进行链接不是一个选项。相反,我们将一个通用的屏幕外UIView.inputAccessoryView设置为我们的原生<InputAccessoryView>层级结构。通过手动告诉这个通用UIView成为第一响应者,该层级结构由响应者基础设施挂载。这个概念在前面提到的博客文章中得到了详细解释。

陷阱

当然,在构建这个API时并非一帆风顺。以下是我们遇到的一些陷阱,以及我们是如何解决它们的。

构建此API的最初想法涉及监听NSNotificationCenter以获取UIKeyboardWill(Show/Hide/ChangeFrame)事件。这种模式在一些开源库以及Facebook应用内部的某些部分中都有使用。不幸的是,UIKeyboardDidChangeFrame事件未能及时被调用,以便在滑动时更新<InputAccessoryView>的帧。此外,这些事件也无法捕获键盘高度的变化。这导致了一类bug,表现如下:

在iPhone X上,文本键盘和表情符号键盘的高度不同。大多数使用键盘事件来操作文本输入框的应用都必须修复上述错误。我们的解决方案是坚持使用.inputAccessoryView属性,这意味着响应者基础设施会这样处理帧更新。


我们遇到的另一个棘手的bug是避免iPhone X上的Home指示条。您可能会想,“Apple正是为此开发了safeAreaLayoutGuide,这很简单!”。我们当时也同样天真。第一个问题是,原生<InputAccessoryView>的实现直到即将出现的那一刻才会有可锚定的窗口。没关系,我们可以覆盖-(BOOL)becomeFirstResponder并在那里强制执行布局约束。遵守这些约束会使辅助视图向上移动,但又出现了一个新的bug:

输入辅助视图成功避开了Home指示条,但现在不安全区域后面的内容可见。解决方案在于这个雷达。我将原生<InputAccessoryView>层级结构封装在一个不符合safeAreaLayoutGuide约束的容器中。原生容器覆盖了不安全区域中的内容,而<InputAccessoryView>则停留在安全区域边界内。


使用示例

这是一个示例,它构建了一个键盘工具栏按钮来重置<TextInput>状态。

class TextInputAccessoryViewExample extends React.Component<
{},
*,
> {
constructor(props) {
super(props);
this.state = {text: 'Placeholder Text'};
}

render() {
const inputAccessoryViewID = 'inputAccessoryView1';
return (
<View>
<TextInput
style={styles.default}
inputAccessoryViewID={inputAccessoryViewID}
onChangeText={text => this.setState({text})}
value={this.state.text}
/>
<InputAccessoryView nativeID={inputAccessoryViewID}>
<View style={{backgroundColor: 'white'}}>
<Button
onPress={() =>
this.setState({text: 'Placeholder Text'})
}
title="Reset Text"
/>
</View>
</InputAccessoryView>
</View>
);
}
}

粘性文本输入的另一个示例可以在仓库中找到。

我何时能使用此功能?

此功能实现的完整提交记录在此<InputAccessoryView>将在即将发布的v0.55.0版本中提供。

愉快地使用键盘 :)