react-hook 应用篇

诞生背景

首先我们要确定,一个新的东西的出现,必定是为了弥补旧的东西的不足,如果旧的东西已经可以完美满足人的需求,那么我们也没必要去绞尽脑汁开发一个新的东西,那么,hooks是在一种什么样的背景下诞生的呢?

react的函数式组件与类式组件有哪些问题与缺陷?

react对于函数式组件和类式组件的定义与用法

react的组件分为两大类,函数式组件与类式组件,函数式组件也称无状态组件,类式组件也称有状态组件。在初学的时候,可能两种组件用的频率都差不多,甚至函数式组件更多一些,因为写起来简单,但是在开发中,类式组件绝对是用的最多的。

函数式组件,其实还有一个别称,也叫展示型组件,顾名思义,也就是只能用来展示内部内容的一个组件。因为开发中,我们往往要用到各种各样的状态,所以函数式组件最多也就是在类式组件都写完后,将类式组件都挂载到一个函数式组件作为展示使用,但是一旦我们还需要使用这个父组件对一些状态或者钩子函数,那么也不得不把这个函数式组件改写成类式组件。

看似函数式组件和类式组件满足了我们的开发需求啊,类式组件写功能,函数式组件集中展示,那么为什么还要推出hooks给函数式组件添加状态和钩子函数呢?其根本原因在于使用类式组件时,不知不觉产生的一些副作用。

类式组件的缺陷

1.整个组件过于冗杂,如果说一个页面的一些逻辑比较复杂,一直在这个组件中去添加状态,添加方法,会导致整个组件代码数量越来越多,非常累赘且不利于阅读。

2.一些可能开发中大家已经不知不觉忽略掉的问题,如方法的this指向问题,生命周期钩子的执行顺序逻辑有些复杂,为组件添加状态的写法不够简洁等等,这些问题在hooks进行了一些优化。

那么hook针对这些缺陷做了哪些改进呢?

总的来说,hook实现了两个优化,一个是代码书写的繁琐程度,一个是让我们完成了组件的复用。

用法介绍

hook究竟做了什么?

首先,我们要对hook进行一个定位,就是hooks究竟是什么?或者说hooks做了什么?

我们从类式组件和函数式组件的差异中不难理解出,hooks如果能做到让函数式组件来替代之前类式组件的地位,那么hooks就一定会至少做这两样工作:为组件添加自己的状态,提供类似生命周期钩子的函数。

那么,先从hooks如何给函数式组件添加自己的状态说起。

最基础的函数式组件

首先看下面一段代码

在这里插入图片描述

这是一个非常基础的函数式组件,我们知道,函数式组件是没有状态的,那么假设我们想要利用这个函数式组件,做一个点击后实现加减效果的功能,我们只能选择这样做,在外部声明一个看似是状态的变量,那么最终实现效果如何呢?

在这里插入图片描述

从控制台的输出我们可以看出,对应的函数确实是执行了,但是页面并没有更新,因为外部声明的变量毕竟不是状态,react是不会进行跟踪的,那么如果我们想要用函数式组件,还想用状态,这时候就需要react-hook了。

用useState与useEffect来改写函数式组件

那么继续看下面的改写代码。

在这里插入图片描述

函数式组件和类式组件比,最欠缺的无非是两点,第一点是状态,第二点是生命周期钩子。在hook中,对于状态,用useState补足,对于生命周期钩子,用useEffect替代,那么具体用法是怎么样的呢?

useState的用法

先看useState,这个方法我们可以传入一个值作为这个状态的初始值,这个方法的返回值是个数组,数组中有两个元素,第一个元素是我们初始化的值,第二个元素是一个函数,这个函数可以用来操作我们设置的这个状态,通常是用解构的方法来取得这两个元素。

useEffect的用法

再看useEffect,这个方法我们可以传入一个函数作为参数,这个函数会在页面更新的时候执行,初次加载的时候也一样会执行,是不是和生命周期的一些钩子用法很类似?那么看看具体的效果吧。

上面写的函数式组件,想要实现点击把数字加减的功能,我们在函数外部声明一个count来进行计数。
在这里插入图片描述

可以看到,通过点击事件触发了我们绑定的方法,这个方法是从useState的返回值中解构出来的,我们可以思考一下,这段代码如果用class写应该怎样写?首先在construstor中声明一下state,然后写一个方法, 方法中先获取state中的状态,再setState改变数值,这么一比较,class是不是麻烦了许多?

代码升级,为函数式组件继续添加状态

我们可以继续为这个代码升级,为他添加另外的状态,看如下代码:

在这里插入图片描述

在新的代码中,为他添加了一个名为style的状态,然后会在鼠标移入时,触发改变背景色的事件,那么实际效果是怎么样的呢?

在这里插入图片描述

添加了多个状态后产生了什么问题?

我们可以看到,这个功能确实是实现了,但是是不是又发现了一点小问题?好像useEffect中传入的函数不管是点击的时候,还是鼠标划入的时候,都是完全执行了啊!那么如果这里面的代码写的很多,或者当我们只想执行一部分的时候难道没法处理了吗?当然是能处理的。

针对多个状态,在useEffect用法上的更改

useEffect的第二个参数

这时候useEffect可以传入的第二个参数就派上了用场,我们可以在第二个参数传入一个数组,这个数组中要放入我们想要监控的状态,这样只有在这个状态要发生改变的时候,才会触发对应的函数去执行,我们将代码做如下的修改

在这里插入图片描述

这里我将number传入了useEffect,这样我们看看最终的效果

在这里插入图片描述

看效果可以看出来了,鼠标划入的时候,颜色改变了并没有触发我们给useEffect传入的函数,只有点击的时候触发了。

为一个组件绑定多个useEffect

同时,一个函数是可以有多个useEffect,可以支撑我们监控多个状态,我们再将代码进行升级。

在这里插入图片描述

这样从代码上看,我们对number和style进行了监控,那么来看一下最终的效果。

在这里插入图片描述

现在就如我们预期的,在对应的状态发生变化的时候,只执行我们想要的代码了。

将useEffect设置成只执行一次应该怎么做?

也要补充一点,如果想要useEffect达到componentDidMount的效果的话,需要将空数组作为第二个参数传递给useEffect,这样的useEffect是只有在初次加载的时候执行,也就是只会执行一次。

hook的复用功能详解

看到这,可能有的人还会有疑问,目前来看,hook只是让写的代码更简洁了,但是功能好像差不多,如果class类式组件用的很熟的话,可能开发效率上也不会相差太多啊。

也就是说,目前来看,我们只看到了hook对于代码繁琐程度的优化,我们不用去很繁琐的创建状态和去修改状态,去记各种生命周期钩子,但是复用这一点还没有见识到。

class想要复用,应该如何处理?

首先我们考虑这样一种情景,比如我们有一个很大型的组件,这个组件有A,B,C三个功能,这三个功能都依赖这个组件的state,如果我想在另一个组件中用到A功能,但是因为A功能对state有依赖,那么就不得不传递这个state,还要复写功能的代码,是不是很繁琐?

hook想要复用,应该如何处理?

那么如果用hook来实现,应该怎么实现呢?这时候就需要用到自定义hook,来看下面这样的一段代码。

在这里插入图片描述

我们声明了一个自定义hook,这个hook有自己的三个状态,isLoading,response,error,我们传入想要请求的地址,和想要建立依赖的数据,当有处理结果的时候,把三个状态值返回,这样在哪里调用了这个hook,只需要看一下返回值的三个状态就可以了,这样我们就完成了功能的完全抽离,在任何地方想要请求数据,都可以直接调用这个hook来实现。

两种方式相互比较有什么优缺点?

反观如果用类式组件,如果状态依赖严重,我们只能传递组件本身的状态,如果依赖不严重,把对应的方法抽离成公共的工具,也需要创建对应的state,可能多个组件里这个类似的属性我们重复创建了几十次,只为了实现一个相同的功能。

hook用法小结

对于hook的用法,总的来说,对于功能抽离成自定义hook,对于组件,用useState与useEffect来为函数式组件添加状态与类似声明周期钩子,这样写法简洁,功能也能复用。

总结

什么是hook?为什么要叫hook?

首先让我们回到初始,hook是什么?或者说,为什么react要给他起这样一个名字?

hook在英语中,是钩子的意思,那么react给他起这个名字,必然不会是乱起的,react是希望这个东西就像一个个小钩子一样,我们在哪里有需要,就把这个钩子钩在哪里。

而且从上面代码中,是不是发现useState,和useEffect这两个方法和我们声明的自定义hook用法很类似?其实useState和useEffect就是react中给我们提供的hook,react还给我们提供了很多的hook,可以自己闲暇的时候去阅读学习一下。

hook的优势

可以说,hook在写法简洁程度和复用代码的程度上,都相对与class有个非常大的提升,但是如果对于习惯了用class类式组件开发的人来说,确实还是很难受的,但是我们要有接受新知识的能力,对于优秀的技术,要能去接纳,才能有一个踏实的立足点。

使用hook的注意事项

适用版本

需要react16.8以上的版本。

tips补充

最后补充两点小tips:

1 不要在循环,条件或嵌套函数中调用Hook,必须始终在React函数的顶层使用Hook。这是因为React需要利用调用顺序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用Hook,就容易导致调用顺序的不一致性,从而产生难以预料到的后果。

2 只能在React函数式组件或自定义Hook中使用Hook。

对于第一点tip的详解

第二点不难理解,第一点可能理解上可能会有点难以理解,为什么循环和分支会出现问题?这是因为,hook是通过执行的顺序来确定状态值的。我们看下面的代码

在这里插入图片描述

这个代码在第一次执行,和第二次执行的时候,执行顺序会有不同,第一次执行的时候,因为showFruit是true,所以三个useState都正常执行了,但是第二次执行的时候,因为shouFruit已经变成了false,所以中间的useState是没有执行的,那么执行结果如何?

在这里插入图片描述

我们可以看到,第二次渲染因为执行顺序不对,会直接导致报错。所以我们在使用时,要注意自己的写法问题,避免这种问题的产生。

补充hook

上下文useContext

使用场景简介

为了方便复用和结构清晰,我们会将一个大的模块拆分成数个组件,这样就难以避免有时候可能会有多层级的包裹,而如果要跨层级传值,使用传统的一层一层传值方式就显得非常的不方便,这种情况就非常适用于useContext。

使用方法概述

具体分为三个步骤。

1.创建context对象。

2.使用context对象的provider属性提供value。

3.使用useContext获取传递的value。

三层嵌套的组件关系应用

下面看一个具体的场景实际应用,首先有三个组件,嵌套三层,代码如下:

父组件:
在这里插入图片描述

子组件:
在这里插入图片描述

孙辈组件:

在这里插入图片描述

如果是传统传参,那么首先要在父组件向子组件传递,再由子组件向孙辈组件传递。但是如果用useContext会使得这个传值过程变得简化许多。

对值的提供者和接受者做相应配置

首先是要对信息的提供者进行相应的配置:

在这里插入图片描述

如上图代码,首先创建上下文对象,然后使用上下文对象的Provider包裹其中的子组件,value为要传递的值。

在这里插入图片描述

子组件首先要引入之前父组件创建的上下文对象,然后使用useContext将上下文对象注入,那么就可以直接跨层级获取到由父辈传递给孙辈的值了。

在这里插入图片描述

和redux有些类似的useReducer

使用场景简介

有时候,我们需要对同一个状态有各种各样的处理,而这时候,如果应用useState虽然可以满足我们的开发需求,但是这样会使得state过于臃肿,且不利于阅读。我们作为程序员,一定要记得,代码虽然是写给机器运行的,但更是写给人看的,useReducer可以让代码变得更利于阅读。

具体用法

useReducer这个钩子,对于对redux比较熟悉的人会非常好理解,直接看下面的代码。

在这里插入图片描述

useReducer和useState的用法非常类似,需要我们传入两个参数第一个参数为我们定义的reducer,第二个参数为我们为状态设定的初始值。

想要触发对应的action也非常简单,直接调用接收的dispatch,传入对应的参数即可触发相应的事件了,效果也和之前使用的useState创建的计数器类似。

为什么要用useReducer?

那么,看到这里,会不会有一个疑问,这样的功能,用useState依然可以实现,那还用useReducer干嘛?但是如果真要我做比较,在useState和useReducer里比较,我个人更倾向于useReducer。

我认为最直接的一点,就是对这个状态相关逻辑代码集中到了一个函数内,更利于测试,维护,和他人阅读。之前的useState创建的计数器,是把逻辑写传入对应状态的处理函数内,但是一旦情况变得多,代码将难以维护,不利于阅读。

还是那句话,代码是写给人看的,写出来的代码只是能运行别人谁也看不懂,那也含无意义。

小结

在我看来,如果我写的这篇文章可以完全理解,那么将hook应用到开发中,最多是刚开始使用会有些不熟悉,但是基本不会出现有无法实现的功能的情况。同时代码也会相比于原来精简不少,这个精简,并不单纯是行数的减少,而是代码的可阅读性,简洁程度。

hook的应用,在我看来分三步,第一步是会用,第二步是优化,第三步是原理的理解。这篇文章中的代码逻辑都比较简单,实际应用中,会有一些复杂的情况,是我们需要对性能进行优化的,性能的优化,永远是前端的重中之重。

这篇文章仅仅作为介绍hook想应用到开发中的入门,后续我会根据自己的学习理解,写优化篇和原理篇,感谢阅读!

发布了1 篇原创文章 · 获赞 0 · 访问量 14
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览