前言
本文为意译,翻译过程中掺杂本人的理解,如有误导,请放弃继续阅读。
原文地址:Introducing Hooks
正文
Hooks是即将到来的React特性。这个特性能让你不需要编写class component的前提下也能使用state和其他React特性。当前,在React v16.7.0-alpha版本中,它们是可用的。
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
复制代码
上面这个函数中的useState
是我们接触到的第一个“Hook”。但是,这个例子也仅仅是一个引子,好戏还在后头呢。如果你对这个例子感到不是很理解,不用急,我们娓娓道来。
在这一章剩余部分,我们将会继续解释一下[我们为什么要往React里面添加Hooks这个特性]和[这个特性如何帮助我们编写出更优秀的应用程序]。
视频介绍
在React Conf 2018大会上,先是Sophie Alpert和Dan Abramov介绍了Hooks,然后Ryan Florence演示了如何使用Hooks来重构你的项目。视频查看这里(是youtube的视频,肯定要翻墙啦)。
没有断崖式的升级
在我们继续介绍之前,首先我们声明一下:
- 完全可选。你可以在新写的组件中使用Hooks,而不是去改写现有的代码(注意,class component里面是不能用Hooks的)。又或者说,现在你不需要马上就去学习或者使用Hooks,先观望一会。
- 100%向后兼容。Hooks没有包含任何breaking changes。
- 现在就可用了。在React v16.7.0-alpha版本中,Hooks已经可以使用了。我们希望在接收到社区的反馈后,在v16.7正式版本中正式引入它(截止2018/12/29,React的v16.7已经发布了,但是没有Hooks特性。官方承认他们“吹牛”了)。
- 目前没有任何把class component写法从React中去除的打算。在这篇文档的底部,你可以阅读更多关于渐进式使用Hooks的介绍。
- Hooks不会取代你现有的React概念。相反,Hooks提供了一个更加直接的API来应用那些你已经掌握的React概念:props,state,context,refs和生命周期函数。随后,我们会向你展示Hooks将这些概念连接起来的强大威力。
动机
Hooks解决了在使用React过程中各种各样表面看起来没有太大关联性的问题。在过去五年,我们编写和维护过成千上万个组件,我们无数次遇到了这种类型问题。无论你是正在学习React,还是已经日常开发中使用React,又或者说使用React-like的类库来开发,你或多或少都会遇到我们所说的这种问题。
1. 状态型逻辑(stateful logic)难以在组件间复用
React并没有为我们提供一种插拔式的方案去复用那些我们本想复用的行为(比如说,把一个组件连接到store)。如果你用过React,你可能对render props
和高阶组件
等封装模式比较熟悉。而这些模式本质上就是在尝试解决组件间代码复用的问题。但是,这些模式都是要求你去重构你的组件树,往组件树中加入不必要的层级。这样做显得有点多余,并且会使得代码变得难以追踪。如果你用React DevTools去查看一个典型的React应用,你会看到所谓的“wrapper hell”-一个组件往往会被多个的provider 组件,consumer组件,高阶组件,render props组件或者其他的抽象层所层层嵌套。虽然,我们可以通过筛选功能将它们屏蔽,但是它们却指出了一个更深层次的问题:React需要一个更好的原子方案来共享状态型逻辑。
目前来看,这个原子方案就是Hooks啦。有了Hooks,你就可以将状态型逻辑从组件种抽离出来,使之能不断地复用和被单独地测试。Hooks能让在不加入多余的层级(也就是说不影响你的组件树结构)的前提下复用你的状态型逻辑。这使得你能够轻易地在组件之间或者社区里面去共享你的代码-Hooks。
2. 复杂的组件变得日益难懂
维护组件过程中,我们经常会碰到这样的情况:一开始,组件是简单易懂的。但是随着时间的推移,这个组件内部会被塞满各种状态型逻辑代码和副作用代码(side effects)。同时,每个生命周期函数也是混杂着各种不相关的逻辑。举个例子,我们会在组件的componentDidMount和componentDidUpdate里面执行一些数据获取的操作。然而,一般情况下我们还会在componentDidMount插入一些与数据获取行为不相关的代码-设置事件监听。然后,为了解除事件监听,我们会在componentWillUnmount里面插入一清除工作的代码。不相关的行为以及其实现代码都被揉杂在一个方法里面了,而相关的行为以及其实现代码却被分离到不同的方法里面。这很容易会带来bug和导致行为的不一致性。
在很多情况下,把上面所提及的组件切割成各个小的组件是不太可能的。因为状态型逻辑遍布组件的各种生命周期函数里面。同时,它们也是难以测试的。这也是很多用户喜欢结合一些状态管理类库来使用React的原因之一。然而,引入过多的抽象层会导致你在阅读代码的时候会不停地在不同的文件之间进行跳转,同时,这也把组件的单元测试搞得复杂了。
译者注:下面这段话是重点。因为它指出了,在没有Hooks之前,我们只能基于组件的生命周期函数来做代码的分离和封装。但是有Hooks之后,我们就可以从自己的关注点(一般是业务代码复用的角度)出发来做真正意义上的“代码封装和共享”,从而实现我们梦想---高内聚,低耦合。
为了解决这些问题,Hooks应运而生。Hooks让你能根据自己的关注点(你觉得哪些代码应该关联在一起的)来将一个大组件分割成各个小的内聚型函数(例如在这个函数里面,你可以将所有跟设置订阅或者获取数据的相关代码真正地关联在一起)。相比以前高阶组件和render props时代的基于生命周期函数来实现关注点分离,如今的Hooks能让你基于自己的关注点定义来分离代码。还有,你也可以选用reducer(React Hooks内置了一个叫useReducer的Hooks)来管理组件的本地状态,这会使得组件状态更加的可预测性。
3. classes(“类”写法)让人和机器都感到困惑
也就是说,“类”写法型的代码,不但人也难以掌握,而且机器也难以优化。
除了让代码变得更加难以复用和组织外,我们发现了classes component的写法对于初学者来说,很有可能是一只巨大的拦路虎。原因如下:
- 你必须要理解“this”在javascript里面的工作原理。而这个工作原理跟“this”在大部分编程语言里面的表现是相比,是大有不同的。
- 你必须要记住为事件处理器绑定正确的“this”上下文。如果你不用ES7的syntax proposals来写,那么你的代码将会变得很臃肿。
很多人能完全理解props,state和自上而下的数据流等React概念,但是就是搞不太懂classes component的写法。即使是在两个开发经验丰富的React使用者身上,关于[function component 和classes component有什么区别]和[什么时候该用function component,什么时候该用classes component]方面的理解也难以达成一致。
此外,React已经面向市场已经大约五年了。我们想保证它还有下一个充满重大意义的五年。据Svelte,Angular,Glimmer和其他一些界面构建技术库所表明那样,对组件的预编译(ahead-of-time compliation)还存在不少性能上的提升空间。尤其是,当组件不被仅仅限定为模板的时候。最近,我们已经使用Prepack来做一些component folding方面的试验,我们已经看到一些有希望的早期结果了。然而,我们发现了从class component写法所延伸开来某些代码模式会使得我们的这种优化回落到较慢的路径上来。classes component写法同时也对一些现代工具产生了不少问题。举个例子说,classes component的代码不能很好地被压缩。同时,它也使得热重变得载碎片化和不可靠。我们想要打造一个能够对代码优化友好的API。
它就是Hooks。Hooks解决了这些问题。它能让你不用编写classes component的情况下就能使用大部分的React feature。概念上来讲,React component几乎等同于函数。Hooks拥抱函数,于此同时也并没有丢弃React一直以来的实用精神(也就是简单易用)。Hooks提供了必要的“安全舱口”(imperative escape hatches),不需要你去学习复杂的函数式或者响应式编程技术就能上手使用。
渐进式的使用方式
官方再次强调:暂时没有任何将classes component写法从React中移除的计划。
我们都知道React开发者都忙于搬砖,没有时间注意到每一个最新发布的API。Hooks是非常新的。在决定要学习和使用它们之前,我们最好再观望一下,等到市面上更多的示例和教程出来了,再决定也不迟。
我们也知道添加一些新的原子特性到React所带来的使用门槛是非常高的。对于一些喜欢问为什么的用户,我们早就为你准备了一份详细的RFC以供参详。在这份RFC里面,我们介绍了诸多动机细节,并从一些特别的视角来阐述一些细节决策的因由和一些相关的先前技术。
有一个关键点我们需要明白的是,Hooks可以跟现有的代码并存,所以你可以在现有的代码基础上渐进式地采用它。我们把这个试验性质的API提前发布到v16.7.0-alpha.x是为了收集一些来自于社区的反馈。假如你对打造React的未来感兴趣的话,欢迎加入反馈的行列中。另外,我们会以开放的方式来迭代,优化Hooks这个API。
最后要再强调的一点是,我们没有必须马上就迁移到Hooks上面来。我们建议避免一切大的重写,尤其是不要对现存的复杂组件进行重写。因为从传统的“Think in React”转变到“Think in Hooks”上来还是需要点心智成本的。所以,以我们之见,我们最好先在一些新的,不太重要的组件上试用一下Hooks,然后确保团队里面的每个成员都习惯这种封装思维再说。另外,再啰嗦一下,当你尝试过使用Hooks之后,不管是正面的还是反面的,不妨给予我们一些反馈。
虽然我们有意向使用Hooks来覆盖所有classes component能覆盖的使用场景,但是我们还是依然会在可以预见的未来对classes component进行支持。在Facebook,我们有成千上万的classes component,我们绝对没有重写它们的计划。相反,我们允许classes component与 Hooks并存,只是开始在新代码中使用Hooks而已。