关于react Hooks你要知道的一切

原创 zhou111222 译文 react 105阅读 2018-12-13 10:47:25 举报

背景

最近react 官方在 2018 ReactConf 大会上宣布 React v16.7.0-alpha(内测) 将引入 Hooks
那么Hooks有什么意义呢?在我最近项目的开发过程中,用到了功能组件和受控组件。但是由于之后版本迭代,功能升级和代码优化,一些功能组件必须要使用到state和生命周期方法。这时我们常规的解决方案为:将功能组件的代码修改为使用React.Componentclass的受控组件,以此来使用只有受控组件才拥有的state和生命周期方法。这个方法非常的麻烦。但是随着Hooks的出现,可以让我们在不编写React.Componentclass的情况下在直接在功能组件中使用state和生命周期方法一样的功能!

ReactHooks-logo

说了这么多,让我们把最近学习到的Hooks由浅入深、循序渐进的分享给大家。本文从以下几个知识点展开学习:

  • 1、功能组件和受控组件
  • 2、state Hook的用法
  • 3、这个useState()法是什么?
  • 4、React组件的生命周期
  • 5、Effect Hook的使用和注意事项
  • 6、结论

一、功能组件和受控组件

在讲组件之前,先介绍一下React组件的两个重要特性:

  • props :组件属性,专门用来连接父子组件间通信,父组件传输父类成员,子组件可以利用但不能编辑父类成员。
  • state :组件状态,专门负责保存和改变组件内部的状态。

现在开始比较一下功能组件和受控组件:

1、功能组件

功能组件内部的状态不受state控制。它是为了创建纯展示组件,这种组件只负责根据传入的props来展示,不涉及到要state状态的操作。,例如:

或这个:

特点:

  • 无状态组件只能访问输入的props,同样的props会得到同样的渲染结果,不会有副作用;
  • 组件不会被实例化,整体渲染性能得到提升;
  • 组件无法访问生命周期的方法;
  • 组件不能访问this对象;

2、受控组件

React.Component是以ES6的形式来创建React的组件的,是React目前极为推荐的创建有状态组件的方式,这种方式创建的组件状态受state控制,我们称之为受控组件,例如:

好了,讲了上面的基础,接下来我们开始由浅入深学习Hooks。

二、state Hook的用法

1、使用useState

假设我们有一个这样的受控组件:组件的状态变化受到state的控制。

使用React 的 useState Hooks,我们能够将没有state状态的功能组件改变成上面的受控组件:

现在让我们回顾一下上面讲到的useState Hook,并逐行分析来我们的理解。

  • 第1行:我们的useState从React中 导入Hook。它允许我们将本地状态保存在功能组件中。
  • 第4行:在Example组件内部,我们通过调用useStateHook 声明一个新的状态变量。它返回一对值,我们给它们命名为count和setCount。我们调用变量count是因为它保存了按钮点击次数。我们通过传递0作为唯一的useState参数将它初始化为零。
    第二个返回的项本身就是一个函数。它让我们更新count,所以我们将其命名setCount。
  • 第9行:当用户点击时,我们运行setCount函数。然后,React将重新呈现Example组件,并将新count值传递给它。
    useState()返回了两个变量,当前状态count和更新它的函数setCount。我们可以把这两个变量命名为任何我们想要的。<br>

在功能组件中,我们可以将count直接拿来使用:

在功能组件中,我们已经拥有了setCount与count作为变量,所以我们并不需要this:

2、使用多个状态变量

将状态变量声明为一对[something, setSomething]也很方便,因为如果我们想要使用多个状态变量,它可以让我们为不同的状态变量赋予不同的名称:

三、这个useState()语法是什么?

当我们声明一个状态变量时,您可能已经注意到方括号:

左侧的名称不是React API的一部分。您可以命名自己的状态变量:

此JavaScript语法称为数组解构。这意味着我们正在做两个新的变量fruit和setFruit,其中fruit为useState返回的第一个值,setFruit是useState返回的第二个值。它相当于这段代码:

当我们用状态变量声明useState时,它返回一对值:一个包含两个项的数组。第一项是当前值,第二项是允许我们更新它的函数。使用[0]和[1]访问它们有点令人困惑,因为它们具有特定的含义。这就是我们使用数组解构的原因。

到此Hooks中的useState Hook已介绍完毕,下一部分我们将继续讲下一个Hooks中的Effect Hook。在介绍Effect Hook前先着重介绍一下React组件的生命周期,只有了解了React组件生命周期的各个函数,才能以这个为基础学习接下来的Effect Hook。

四、React组件的生命周期

React在组件创建、销毁、运行的过程中会触发组件生命周期各个方法的调用方法中带有前缀 will 的在特定环节之前被调用,而带有前缀 did 的方法则会在特定环节之后被调用。如图所示:

Reactlife

从图中,我们可以得出以下结论:

  • getDefaultProps(),调用1次
  • getInitialState(),调用1次
  • componentWillMount(),调用1次
  • render(),调用>=1次
  • componentDidMount():仅客户端,调用1次
  • componentWillReceiveProps(object nextProps),调用>=0次
  • ShouldComponentUpdate(object nextProps, object nextState),调用>=0次
  • componentWillUpdate(object nextProps, object nextState),调用>=0次
  • componentDidUpdate(object prevProps, object prevState),调用>=0次
  • componentWillUnmount(),调用1次

至此React 组件的生命周期的基础已介绍完毕,在此基础上,大家可以更容易理解、学习Effect Hook。

五、Effect Hook的使用和注意事项

useEffect 可以利用我们组件中的 local state 进行一些带有副作用的操作,,而无需使用仅在类组件中可用的生命周期方法。

提示: 如果你熟悉React类生命周期方法,则可以将useEffect Hook视为componentDidMountcomponentDidUpdatecomponentWillUnmount的组合。

React组件中有两种常见的副作用:那些不需要清理的副作用,以及那些需要清理的副作用。让我们通过例子看一下它们的区别。

1、无需清理的副作用

有时,我们希望在React更新DOM之后运行一些额外的代码。网络请求,手动突变DOM和日志记录是不需要清理的副作用的常见示例。我们这样说是因为我们可以运行它们并立即忘记它们,让我们比较一下受控组件与功能组件怎样表达副作用:

使用受控组件的例子

在受控组件中,我们将副作用放入componentDidMount和componentDidUpdate。回到我们的示例,这里是一个React计数器类组件,它在React对DOM进行更改后立即更新文档标题。

现在让我们看看我们如何使用useEffectHook 做同样的事情。

使用Effect Hook的例子

1.useEffect有什么作用?

通过使用Hook,你将函数(我们将其称为“效果”)作为参数传递给useEffect,告诉React你的组件需要在渲染后执行某些操作。React将记住你传递的函数,并在确定DOM更新后调用它。在这个例子中,我们效果是设置文档标题,我们的效果也可以执行数据提取或调用其他命令式API。

2.为什么在组件内调用useEffect?

在组件中使用useEffect让我们可以直接从效果中访问状态变量(如count或任何道具)。我们不需要特殊的API来读取它 - 它已经在函数范围内了。Hooks拥抱JavaScript闭包,并避免在JavaScript已经提供解决方案的情况下引入特定于React的API。

3.useEffect 什么时候执行?

它会在组件 mount 和 unmount 以及每次重新渲染的时候都会执行,也就是会在 componentDidMount、componentDidUpdate、componentWillUnmount 这三个时期执行。

2、需要清理的副作用

之前,我们研究了如何表达不需要任何清理的副作用。但是,有些效果需要清理。例如,我们可能希望设置对某些外部数据源的订阅。在这种情况下,清理是非常重要的,这样我们就不会引入内存泄漏!让我们比较一下我们如何使用类和Hooks来实现它。

使用受控组件的例子

在React类中,我们的项目通常会在componentDidMount中设置订阅,并在componentWillUnmount中清除它。例如,假设我们有一个ChatAPI模块,可以让我们订阅朋友的在线状态。接下来看一下如何使用类订阅和显示该状态:

注意componentDidMount和componentWillUnmount如何相互作用。生命周期方法迫使我们拆分这个逻辑,即使它们中的概念代码都与相同的效果有关。

使用hooks的例子
您可能认为我们需要单独的效果来执行清理。但是添加和删除订阅的代码是如此紧密相关,useEffect旨在将其保持在一起。如果你需要清理副作用,你只需要在你的效果中返回一个带有清理功能的函数,React将在清理时运行它:

1.为什么我们从效果中返回一个函数?

这是效果的可选清理机制。每个效果都可能返回一个在它之后清理的函数。这使我们可以保持添加和删除彼此接近的订阅的逻辑。

2.React什么时候清理效果?

当组件卸载时,React执行清理。但是,正如我们之前所了解的那样,效果会针对每个渲染运行而不仅仅是一次。这就是React在下次运行效果之前还清除前一渲染效果的原因。

注意 我们不必从效果中返回命名函数。我们在这里只是为了说明才加的命名,但你可以返回箭头函数。

3、使用useEffec的注意事项

我们只能在 函数组件 中使用 Hooks,我们也可以在一个组件中使用多组 Hooks。比如:

但是这里有一点需要我们注意的就是 我们只能在顶层代码(Top Level)中调用 Hooks,不能在循环或判断语句等里面调用,这样是为了让我们的 Hooks 在每次渲染的时候都会按照 相同的顺序 调用,因为这里有一个很关键的问题,那就是 useState 需要依赖参照第一次渲染的调用顺序来匹配对应的state,否则 useState 会无法正确返回它对于的state。

六、总结

Hooks让我们的函数组件的功能得到了扩充,拥有了和类组件相似的功能,甚至避免了类组件存在的各种问题,那么就会出现各种的疑问,比如:Hooks 引进后, 函数组件 和 类组件 该如何选择?官方关于类似的问题的答复是:

官方的目标是尽可能快的让 Hooks 去覆盖所有的类组件案例,但是现在 Hooks 还处于一个非常早的阶段,各种调试工具、第三方库等都还没有做好对 Hooks 的支持,而且目前也没有可以取代类组件中 getSnapshotBeforeUpdate 和 componentDidCatch 生命做起的 Hooks,不过很快会加上他们。总的来时就是鼓励大家在以后使用 Hooks,对于已存在的类组件不必大规模的去重写,Hooks及Hooks的生态会继续完善,请期待。

至此,关于React Hooks的介绍已到此介绍,感兴趣的同学可关注我们的公众号,跟进后续文章。

参考文献

https://scotch.io/tutorials/getting-started-with-react-hooks#toc-what-are-hooks-

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

https://blog.csdn.net/hesongGG/article/details/83582250

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复