- 原文地址:Making Sense of React Hooks
- 原文做者:Dan Abramov
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:HaoChuan9421
- 校对者:calpa, Ivocin
本周,Sophie Alpert 和我在 React Conf 上提出了 “Hooks” 提案,紧接着是 Ryan Florence 对 Hooks 的深刻介绍:html
我强烈推荐你们观看这个开场演讲,在这个演讲里,你们能够了解到咱们尝试使用 Hooks 提案去解决的问题。不过,花费一个小时看视频也是时间上的巨大投入,因此我决定在下面分享一些关于 Hooks 的想法。前端
注意:Hooks 是 React 的实验性提案。你无需如今就去学习它们。另请注意,这篇文章包含个人我的意见,并不必定表明 React 团队的立场。react
咱们知道组件和自上而下的数据流能够帮助咱们将庞大的 UI 组织成小型、独立、可复用的块。可是,咱们常常没法进一步拆分复杂的组件,由于逻辑是有状态的,并且没法抽象为函数或其余组件。这也就是人们有时说 React 不容许他们“分离关注点”的意思。android
这些状况很常见,包括动画、表单处理、链接到外部数据源以及其余不少咱们但愿在组件中作的事情。当咱们尝试单独使用组件来解决这些问题时,一般咱们会这样收场:ios
咱们认为 Hooks 是解决全部这些问题的最佳实践。Hooks 让咱们将组件内部的逻辑组织成可复用的隔离单元:git
Hooks 在组件内部应用 React 的哲学(显式数据流和组合),而不只仅是组件之间。这就是为何我以为 Hooks 天生就适用于 React 的组件模型。github
不一样于 render props 或高阶组件等的模式,Hooks 不会在组件树中引入没必要要的嵌套。它们也没有受到 mixins 的负面影响。spring
即便你心里一开始是抵触的(就像我刚开始同样!),我仍是强烈建议你直接对这个提案进行一次尝试和探索。我想你会喜欢它的。编程
在咱们详细介绍 Hooks 以前,你可能会担忧咱们经过 Hooks 只是向 React 添加了更多概念。这是一个公正的批评。我认为虽然学习它们确定会有短时间的认知成本,不过最终的结果却偏偏相反。后端
若是 React 社区接受 Hooks 的提案,这将减小编写 React 应用时须要考虑的概念数量。Hooks 可使得你始终使用函数,而没必要在函数、类、高阶组件和 reader 属性之间不断切换。
就部署大小而言,对 Hooks 的支持仅仅增长了 React 约 1.5kB(min + gzip)的大小。虽然很少,但因为使用 Hooks 的代码一般能够比使用类的等效代码压缩得更小,因此使用 Hooks 也可能会减小你的包大小。下面这个例子有点极端,但它有效地展现了我这么说的缘由(点击查看整个帖子):
Hooks 提案不包括任何重大变化。即便你在新编写的组件中采用了 Hooks,你现有的代码仍将照常运行。事实上,这正是咱们推荐的 —— 不作大的重写!在任何关键代码中采用 Hooks 都是一个好主意。与此同时,若是你可以尝试 16.7 alpha 版并在 Hooks proposal 和 report any bugs 向在咱们提供反馈,咱们将不胜感激。
要了解 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 的本地状态来保存当前窗口宽度,并在窗口调整大小时使用一个反作用来设置该状态:
就像你从上面看到的那样,像 useState 和 useEffect 这样做为基本构建块的 React 内置 Hooks。咱们能够直接在组件中使用它们,或者咱们能够将它们整合到自定义 Hooks 中,就像 useWindowWidth 那样。使用自定义 Hooks 感受就像使用 React 的内置 API 同样驾轻就熟。
你能够从此概述中了解有关内置 Hooks 的更多信息。
Hooks 是彻底封装的 —— 你每次调用 Hooks 函数, 它都会从当前执行组件中获取到独立的本地状态。对这个特殊的例子来讲并不重要(全部组件的窗口宽度是相同的!),但这正是 Hooks 如此强大的缘由。它们不只是一种共享状态的方式,更是共享状态化逻辑的方式。咱们不想破坏自上而下的数据流!
每一个 Hooks 均可以包含一些本地状态和反作用。你能够在不一样 Hooks 之间传值,就像在一般在函数之间作的那样。Hooks 能够接受参数并返回值,由于它们就是JavaScript 函数。
这是一个实验 Hooks 的 React 动画库的例子:
注意,在演示代码中,这个惊人的动画是经过几个自定义 Hooks 的传值实现的。
(若是你想了解更多关于这个例子的信息, 查看此介绍。)
在 Hooks 之间传递数据的能力使得它们很是适合实现动画、数据订阅、表单管理和其余状态化的抽象。不一样于 render props 和高阶组件,Hooks 不会在渲染树中建立“错误层次结构”。它们更像是一个链接到组件的“存储单元”的平面列表。没有额外的层。
在咱们看来,自定义 Hooks 是 Hooks 提案中最吸引人的部分。可是为了使自定义 Hooks 工做,React 须要为函数提供一种声明状态和反作用的办法。而这也正是像 useState 和 useEffect 这样的内置 Hooks 容许咱们作的事情。你能够在文档中了解它们。
事实证实,这些内置 Hooks 不只可用于建立自定义 Hooks。它们也足以用来定义组件,由于它们像 state 同样为咱们提供了全部必要的特性。这就是为何咱们但愿 Hooks 成为将来定义 React 组件的主要缘由。
咱们没有打算弃用类。在 Facebook,咱们有成千上万的类组件,并且和你同样,咱们无心重写它们。可是若是 React 社区接受了 Hooks,那么同时推荐两种不一样的方式来编写组件是没有意义的。Hooks 能够涵盖类的全部应用场景,同时在抽象,测试和复用代码方面提供更大的灵活性。这就是为何 Hooks 表明了咱们对 React 将来的愿景。
你可能会对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 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。