[译] 理解 React Hooks

本周,Sophie Alpert 和我在 React Conf 上提出了 “Hooks” 提案,紧接着是 Ryan Florence 对 Hooks 的深刻介绍:html

我强烈推荐你们观看这个开场演讲,在这个演讲里,你们能够了解到咱们尝试使用 Hooks 提案去解决的问题。不过,花费一个小时看视频也是时间上的巨大投入,因此我决定在下面分享一些关于 Hooks 的想法。前端

注意:Hooks 是 React 的实验性提案。你无需如今就去学习它们。另请注意,这篇文章包含个人我的意见,并不必定表明 React 团队的立场react

为何须要 Hooks?

咱们知道组件和自上而下的数据流能够帮助咱们将庞大的 UI 组织成小型、独立、可复用的块。可是,咱们常常没法进一步拆分复杂的组件,由于逻辑是有状态的,并且没法抽象为函数或其余组件。这也就是人们有时说 React 不容许他们“分离关注点”的意思。android

这些状况很常见,包括动画、表单处理、链接到外部数据源以及其余不少咱们但愿在组件中作的事情。当咱们尝试单独使用组件来解决这些问题时,一般咱们会这样收场:ios

  • 巨大的组件 难以重构和测试。
  • 重复的逻辑 在不一样的组件和生命周期函数之间。
  • 复杂的模式 像 render props 和高阶组件。

咱们认为 Hooks 是解决全部这些问题的最佳实践。Hooks 让咱们将组件内部的逻辑组织成可复用的隔离单元git

Hooks 在组件内部应用 React 的哲学(显式数据流和组合),而不只仅是组件之间。这就是为何我以为 Hooks 天生就适用于 React 的组件模型。github

不一样于 render props 或高阶组件等的模式,Hooks 不会在组件树中引入没必要要的嵌套。它们也没有受到 mixins 的负面影响spring

即便你心里一开始是抵触的(就像我刚开始同样!),我仍是强烈建议你直接对这个提案进行一次尝试和探索。我想你会喜欢它的。编程

Hooks 会使 React 变得臃肿吗?

在咱们详细介绍 Hooks 以前,你可能会担忧咱们经过 Hooks 只是向 React 添加了更多概念。这是一个公正的批评。我认为虽然学习它们确定会有短时间的认知成本,不过最终的结果却偏偏相反。后端

若是 React 社区接受 Hooks 的提案,这将减小编写 React 应用时须要考虑的概念数量。Hooks 可使得你始终使用函数,而没必要在函数、类、高阶组件和 reader 属性之间不断切换。

就部署大小而言,对 Hooks 的支持仅仅增长了 React 约 1.5kB(min + gzip)的大小。虽然很少,但因为使用 Hooks 的代码一般能够比使用类的等效代码压缩得更小,因此使用 Hooks 也可能会减小你的包大小。下面这个例子有点极端,但它有效地展现了我这么说的缘由(点击查看整个帖子):

Hooks 提案不包括任何重大变化。即便你在新编写的组件中采用了 Hooks,你现有的代码仍将照常运行。事实上,这正是咱们推荐的 —— 不作大的重写!在任何关键代码中采用 Hooks 都是一个好主意。与此同时,若是你可以尝试 16.7 alpha 版并在 Hooks proposalreport any bugs 向在咱们提供反馈,咱们将不胜感激。

究竟什么是 Hooks?

要了解 Hooks,咱们须要退一步来思考代码复用。

今天,有不少方式能够在 React 应用中复用逻辑。咱们能够编写一个简单的函数并调用它们来进行某些计算。咱们也能够编写组件(它们自己能够是函数或类)。组件更强大,但它们必须渲染一些 UI。这使得它们不便于共享非可视逻辑。这使得咱们最终不得不用到 render props 和高阶组件等复杂模式。若是只用一种简单的方式来复用代码而不是那么多,那么React会不会简单点

函数彷佛是代码复用的一种完美机制。在函数之间组织逻辑仅须要最少的精力。可是,函数内没法包含 React 的本地状态。在不重构代码或不抽象出 Observables 的状况下,你也没法从类组件中抽象出“监视窗口大小并更新状态”或“随时间变化改变更画值”的行为。这两种方法都破坏了咱们喜欢的 React 的简单性。

Hooks 正好解决了这个问题。 Hooks 容许你经过调用单个函数以在函数中使用 React 的特性(如状态)。React 提供了一些内置的 Hooks,它们暴露了 React 的“构建块”:状态、生命周期和上下文。

因为 Hooks 是普通的 JavaScript 函数,所以你能够将 React 提供的内置 Hooks 组合到你本身的“自定义 Hooks”中。这使你能够将复杂问题转换为一行代码,并在整个应用或 React 社区中分享它们:

注意,自定义 Hooks 从技术上讲并非 React 的特性。编写自定义 Hooks 的可行性源自于 Hooks 的设计方式。

来点代码!

假设咱们想要将订阅一个自适应当前窗口宽度的组件(例如,在有限的视图上显示不一样的内容)。

如今你有几种方法能够编写这种代码。这些方法包括编写类,设置一些生命周期函数,若是要在组件之间复用,甚至能够须要提取 render props 或更高一层的组件。但我认为没有比这更好的了:

若是你看这段代码,它偏偏就是我所表达的。咱们在咱们的组件中使用窗口的宽度,而 React 将会在它变化是从新渲染。这就是 Hooks 的目的 —— 使组件作到真正的声明式,即便它们包含状态和反作用。

让咱们来看看如何实现这个自定义 Hooks。咱们使用 React 的本地状态来保存当前窗口宽度,并在窗口调整大小时使用一个反作用来设置该状态:

就像你从上面看到的那样,像 useStateuseEffect 这样做为基本构建块的 React 内置 Hooks。咱们能够直接在组件中使用它们,或者咱们能够将它们整合到自定义 Hooks 中,就像 useWindowWidth 那样。使用自定义 Hooks 感受就像使用 React 的内置 API 同样驾轻就熟。

你能够从此概述中了解有关内置 Hooks 的更多信息。

Hooks 是彻底封装的 —— 你每次调用 Hooks 函数, 它都会从当前执行组件中获取到独立的本地状态。对这个特殊的例子来讲并不重要(全部组件的窗口宽度是相同的!),但这正是 Hooks 如此强大的缘由。它们不只是一种共享状态的方式,更是共享状态化逻辑的方式。咱们不想破坏自上而下的数据流!

每一个 Hooks 均可以包含一些本地状态和反作用。你能够在不一样 Hooks 之间传值,就像在一般在函数之间作的那样。Hooks 能够接受参数并返回值,由于它们就是JavaScript 函数。

这是一个实验 Hooks 的 React 动画库的例子:

在 CodeSandbox 上运行这个例子

注意,在演示代码中,这个惊人的动画是经过几个自定义 Hooks 的传值实现的。

(若是你想了解更多关于这个例子的信息, 查看此介绍。)

在 Hooks 之间传递数据的能力使得它们很是适合实现动画、数据订阅、表单管理和其余状态化的抽象。不一样于 render props 和高阶组件,Hooks 不会在渲染树中建立“错误层次结构”。它们更像是一个链接到组件的“存储单元”的平面列表。没有额外的层。

类又该何去何从?

在咱们看来,自定义 Hooks 是 Hooks 提案中最吸引人的部分。可是为了使自定义 Hooks 工做,React 须要为函数提供一种声明状态和反作用的办法。而这也正是像 useStateuseEffect 这样的内置 Hooks 容许咱们作的事情。你能够在文档中了解它们。

事实证实,这些内置 Hooks 不只可用于建立自定义 Hooks。它们足以用来定义组件,由于它们像 state 同样为咱们提供了全部必要的特性。这就是为何咱们但愿 Hooks 成为将来定义 React 组件的主要缘由。

咱们没有打算弃用类。在 Facebook,咱们有成千上万的类组件,并且和你同样,咱们无心重写它们。可是若是 React 社区接受了 Hooks,那么同时推荐两种不一样的方式来编写组件是没有意义的。Hooks 能够涵盖类的全部应用场景,同时在抽象,测试和复用代码方面提供更大的灵活性。这就是为何 Hooks 表明了咱们对 React 将来的愿景。

不过 Hooks 是否是有点“魔术化”?

你可能会对Hooks 的规则感到惊讶。

虽然必须在顶层调用 Hooks 是不寻常的,但即便能够,你可能也不但愿在某种条件判断中定义状态。例如,你也没法对类中定义的状态进行条件判断,而在过去四年和 React 用户的交流中,我也没有听到过对此的抱怨。

这种设计在不引入额外的语法噪音或其余坑的状况下,对自定义 Hooks 相当重要。咱们知道用户一开始可能不熟悉,但咱们认为这种取舍对将来是值得的。若是你不一样意,我鼓励你动手去实践一下,看看这是否会改变你的感觉。

咱们已经在生产环境下使用 Hooks 一个月了,以观察工程师们是否对这些规则感到困惑。咱们发现实际状况是人们会在几个小时内习惯它们。就我的而言,我认可这些规则起初让我“感受不对”,但我很快就克服了它。此次经历像极了我对 React 的第一印象。(你一开始就喜欢 React 吗?至少我不是一开始就喜欢,直到更屡次尝试后才改变见解。)

记住,在 Hooks 的实现中也没有什么“魔术”。就像 Jamie 指出的那样,它像极了这个:

咱们为每一个组件保留了一个 Hooks 列表,并在每次 Hooks 被调用时移动到列表中的下一项。得意于 Hooks 的规则,它们的顺序在每次渲染中都是相同的,所以咱们能够为每次调用提供正确的组件状态。要知道 React 不须要作任何特殊处理就能知道哪一个组件正在渲染 —— 调用你的组件的正是 React。

也许你在想 React 在哪里保存了 Hooks 的状态。答案就是,它保存在和 React 为类保持状态相同位置。不管你如何定义组件,React 都有一个内部的更新队列,它是任何状态的真实来源。

Hooks 不依赖于现代 JavaScript 库中常见的代理或 getter。按理说,Hooks 比一些解决相似问题的流行方法日常。我想说 Hooks 就像调用 array.push 和 array.pop 同样普通(同样的取决于调用顺序!)。

Hooks 的设计与 React 无关。事实上,在提案发布后的前几天,不一样的人提出了针对 Vue,Web Components 甚至原生 JavaScript 函数的相同 Hooks API 的实验性实现。

最后,若是你是一个纯函数编程主义者而且对 React 依赖可突变状态的实现细节感到不安,你会欣喜的发现完成 Hooks 能够以函数式编程的方式实现(若是 JavaScript 支持它们)。固然,React 一直依赖于内部的可突变状态 —— 正因如此没必要那样作。

不管你是从一个更务实仍是教条的角度来考虑(或者你二者兼有),我但愿至少有一个立场是有意义的。最重要的是,我认为 Hooks 让咱们用更少的精力去构建组件,并提供更好的用户体验。这就正是我我的对 Hooks 感到兴奋的地方。

传播正能量,而不是炒做

若是 Hooks 对你尚未什么吸引力,我彻底能够理解。我仍然但愿你能在一个很小的项目上尝试一下,看看是否会改变你的见解。不管你是遇到须要 Hooks 来解决的问题,仍是说你有不一样的解决方案,欢迎经过 RFC 告诉咱们!

若是我你感到兴奋,或者说有那么点好奇,那就太好了!我只有一个问题要问。如今有不少人正在学习 React,若是咱们匆匆忙忙的编写教程,并把仅仅才出现几天的功能宣称为最佳实践,他们会感到困惑。即便对咱们在 React 团队的人来讲,关于 Hooks 的一些事情还不是很清楚。

若是你在 Hooks 不稳按期间开发了任何有关 Hooks 的内容,请突出提示它们是一个实验性提案,并包含指向官方文档的连接。咱们会在提案发生任何改变时及时更新它。咱们也花了至关多的精力来完善它,因此不少问题已在那里获得了解决。

当你和其余不像你那么兴奋的人交流时,请保持礼貌。若是你发现别人对此有误解,若是对方乐意的话你能够和他分享更多信息。但任何改变都是可怕的,做为一个社区,咱们应该尽力帮助人们,而不是疏远他们。若是我(或 React 团队中的任何其余人)未遵循此建议,请致电咱们!

更进一步

查看 Hooks 提案的文档以了解更多信息:

Hooks 仍然处于早期阶段,但咱们很乐意能听到大家的反馈。你能够直接去 RFC,与此同时,咱们也会尽可能及时回复 Twitter 上的对话。

若是有不清楚的地方,请告诉我,我很乐意为你答疑解惑。感谢你的阅读!

Vitra — Portemanteau Hang it all

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索