[关闭]
@qinyun 2018-10-26T10:42:44.000000Z 字数 5617 阅读 792

React将引入Hooks,你怎么看?

未分类


今天,在2018 ReactConf 大会上,React官方宣布React v16.7.0-alpha将引入Hooks,乍一看,你可能在想Hooks是什么?有什么用?且看下文分析。

Hooks是什么?

Hooks是一种函数,该函数允许你“勾住(hook into)”React状态和来自函数组件的生命周期功能。Hook在类内部不起作用,它们允许你无需类就使用React。(不建议你马上开始重写你现有的组件,但你可以在新组件中开始使用Hook。)

React提供了一些内置Hook,如useState,你也可以创建自定义Hooks以在不同的组件中复用有状态行为。

根据React官方给出的文档,Hooks主要分为以下几种:

State Hook

该示例显示了一个计数器,点击该按钮,值会递增。

  1. import { useState } from 'react';
  2. function Example() {
  3. // Declare a new state variable, which we'll call "count"
  4. const [count, setCount] = useState(0);
  5. return (
  6. <div>
  7. <p>You clicked {count} times</p>
  8. <button onClick={() => setCount(count + 1)}>
  9. Click me
  10. </button>
  11. </div>
  12. );
  13. }

在这里,useState是一个Hook,我们在函数组件中调用它以给它添加一些本地状态。React将在重新渲染之间保留这个状态。useState返回一对值:currentstate值和允许你更新它的函数。

可以从事件处理程序或其他位置调用该函数,这与类中的this.setState类似,除了其不会把旧的和新的状态合并在一起。

声明多个状态变量

可以在单个组件中多次使用State Hook:

  1. function ExampleWithManyStates() {
  2. // Declare multiple state variables!
  3. const [age, setAge] = useState(42);
  4. const [fruit, setFruit] = useState('banana');
  5. const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  6. // ...
  7. }

数组解构语法允许我们给状态变量取不同的名字,这些变量是我们通过调用useState声明的,这不是useState API的一部分。相反,React假设如果多次调用useState,那么在每次渲染的时候要遵循相同的顺序来做。

Effect Hook

你之前很可能已经执行了数据提取、订阅、或手工改变来自React组件的DOM。我们称这些操作为“副作用(side effect)”,因为它们会影响其他组件,并且在渲染过程中无法完成。

Effect Hook、useEffect,增加了从函数组件执行副作用的功能。它与React类中的componentDidMount、componentDidUpdate、和componentWillUnmount有相同的功能,但是统一为单个API。(我们将展示一个例子,该示例在Using the Effect Hook中对useEffect和这些方法进行比较。)

例如,此组件在React更新DOM后设置文档标题:

  1. import { useState, useEffect } from 'react';
  2. function Example() {
  3. const [count, setCount] = useState(0);
  4. // Similar to componentDidMount and componentDidUpdate:
  5. useEffect(() => {
  6. // Update the document title using the browser API
  7. document.title = `You clicked ${count} times`;
  8. });
  9. return (
  10. <div>
  11. <p>You clicked {count} times</p>
  12. <button onClick={() => setCount(count + 1)}>
  13. Click me
  14. </button>
  15. </div>
  16. );
  17. }

当你调用useEffect时,你通知React在刷新对DOM的更改后运行你的“effect”函数。Effect在组件内声明,因此可以访问其props和state。默认情况下,React在每次渲染后运行effect,包括第一次渲染。

效果还可以通过返回一个函数来指定它们之后如何“清理”。例如,此组件使用effect来订阅朋友的在线状态,并通过取消订阅来清理:

  1. import { useState, useEffect } from 'react';
  2. function FriendStatus(props) {
  3. const [isOnline, setIsOnline] = useState(null);
  4. function handleStatusChange(status) {
  5. setIsOnline(status.isOnline);
  6. }
  7. useEffect(() => {
  8. ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  9. return () => {
  10. ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  11. };
  12. });
  13. if (isOnline === null) {
  14. return 'Loading...';
  15. }
  16. return isOnline ? 'Online' : 'Offline';
  17. }

在此示例中,当组件卸载时,以及在由于后续渲染而重新运行effect之前,React将取消订阅我们的ChatAPI。(如果我们传递给ChatAPI的props.friend.id没有变化,有办法让React跳过重新订阅。)就像使用useState一样,可以在组件中使用多个effect:

  1. function FriendStatusWithCounter(props) {
  2. const [count, setCount] = useState(0);
  3. useEffect(() => {
  4. document.title = `You clicked ${count} times`;
  5. });
  6. const [isOnline, setIsOnline] = useState(null);
  7. useEffect(() => {
  8. ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  9. return () => {
  10. ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  11. };
  12. });
  13. function handleStatusChange(status) {
  14. setIsOnline(status.isOnline);
  15. }
  16. // ...

Hook允许你通过部件的关联情况(例如添加和删除订阅)来组织组件中的副作用,而不是基于生命周期方法强制拆分。

自定义Hooks

自定义Hooks主要用来复用组件逻辑,详情请阅读:https://reactjs.org/docs/hooks-overview.html

Hook的规则

Hook是JavaScript函数,但强加了两个额外的规则:

这里(https://www.npmjs.com/package/eslint-plugin-react-hooks)提供了一个linter插件来自动执行这些规则,这些规则乍看起来有些令人疑惑,但它们对Hook的良好运行至关重要。

为什么要在React中引入Hooks?

至于为何要在React中引入Hooks,React官方给出了答案:

Hooks解决了React中各种看似不相关的问题,这些问题是我们在开发和维护数以万计的组件时遇到的。无论你是在学习React,还是每天在使用它,还是选择使用具有类似组件模型的其他库,你都可能会发现这类问题。

难以在组件之间重用有状态逻辑

React没有提供可将可重用行为“附加”到组件的方法。如果你用过React一段时间,可能会对渲染prop和高阶组件等模式比较熟悉,它们试图解决这个问题。但是这些模式要求你在使用它们时重构组件,这可能很麻烦,并且代码会变得难以维护。

如果你看一下React DevTools中的典型React应用程序,你可能会发现“包装器地狱”,组件被层层的提供者、消费者、高阶组件、渲染prop和其他抽象组件组包围。虽然我们可以在DevTools中过滤掉它们,但这反应了一个更深层次的问题:React需要一个更好的原语来共享有状态逻辑。

使用Hooks,你可以从组件中提取有状态逻辑,可以进行独立测试和重用。Hooks允许你在不更改组件层次结构的情况下重用有状态逻辑。这样可以轻松地在多个组件之间或与社区共享Hooks。

复杂的组件变得难以理解

我们通常维护的组件都先从简单的开始,然后逐渐加入无法管理的状态逻辑和副作用。每个生命周期方法通常包含不相关逻辑的混合。例如,组件可能会在componentDidMount和componentDidUpdate时获取数据。不过,相同的componentDidMount方法可能还包含一些设置事件监听器的无关逻辑,并在componentWillUnmount中执行清理工作。一起更改的相互关联的代码会被拆分,但完全不相关的代码最终会组合在一个方法中。这样就很容易引入错误和导致不一致。

在很多情况下,很难将这些组件分解为更小的组件,因为状态逻辑到处都是。测试它们也很困难。这是很多人更喜欢将React与单独的状态管理库相结合的原因之一。不过,这通常会引入太多的抽象,要求你在不同的文件之间跳转,让重用组件变得更加困难。

为了解决这个问题,Hooks让你可以根据相关的部分将一个组件拆分为较小的函数,而不是基于生命周期方法进行强制拆分。你还可以选择使用reducer来管理组件的本地状态,以使其更具可预测性。

类让人和机器都感到困惑

根据我们的观察,类是学习React最大的障碍。你必须了解JavaScript中的this是怎么回事,这与它在大多数语言中的使用方法有很大不同。你必须记住绑定事件处理程序。人们可以很好地理解prop、状态和自上而下的数据流,但对类的使用仍然感到很挣扎。React中的函数和类组件之间的区别以及何时使用哪个组件在经验丰富的React开发人员之间也存在分歧。

另外,React已经推出了大约五年时间,Facebook希望React在未来五年充满活力。正如Svelte、Angular、Glimmer和其他项目所表明的那样,预编译组件具有很大的潜力。最近,我们一直在尝试使用Prepack(https://prepack.io/)进行组件折叠,我们已经看到了充满希望的结果。不过,我们发现类组件的无意识模式可能会使这些优化回退。在类上应用现今的一些工具时也存在一些问题。例如,类难以进行minify,并且类会导致热重载变得不可靠。我们希望提供一种API,使代码更有可能保持在可优化的路径上。

为了解决这些问题,Hooks让你可以在不使用类的情况下使用更多的React特性。从概念上讲,React组件很接近于函数。Hooks拥抱函数,但不会以牺牲React精神为代价。Hooks提供了命令式的编程方式,不需要你学习复杂的函数式或反应式编程技术。

例子:https://reactjs.org/docs/hooks-overview.html

逐步采用策略

我们知道,React开发人员专注于产品,他们没有时间研究发布的每个新的API。Hooks是新东西,在学习或采用它们之前先等待更多的示例和教程,这样可能会更好。

我们也知道为React添加新原语的标准是非常高的。对于好奇的读者,这里准备了一个详细的RFC(https://github.com/reactjs/rfcs/pull/68),其中包含更多的细节,并为特定设计决策和现有相关技术提供了额外的视角。

关键的是,Hooks可以与现有代码同时存在,因此你可以逐步采用它们。React官方正在分享这个实验性的API,以便从社区中那些有兴趣打造React未来的人那里获得早期反馈,将公开进行Hooks的迭代。

最后,没有必要急于使用Hooks。建议避免任何“重大的重写”,特别是对于复杂的现有类组件来说。进入“Hooks思考”模式需要精神上的转变。根据经验,最好先在非关键的新组件中练习使用Hooks,并确保团队中的每个人都能适应。

React官方打算让Hooks涵盖所有与类相关的用例,但在可预见的未来,他们将继续支持类组件。在Facebook有数万个类组件,他们表示绝对没有计划重写它们。相反,他们开始在新代码中使用Hooks。

怎样使用Hooks?

假设我们正在尝试编写一个Counter组件,正常的实现如下:

而基于Hooks的实现将如下:

使用Hooks的好处非常明显:

参考链接

https://reactjs.org/docs/hooks-intro.html#motivation

https://reactjs.org/docs/hooks-overview.html

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注