在Facebook以及社区成员的大力推广下,如今React已经几乎成为前端开发的必备技能。但随着对版本的不断优化和迭代,React官方决定逐步废弃 Class Component 的API,全面拥抱 Function Component 的API 即基于函数的钩子 Hook 。这一趋势也是在意料之中,React建立之初本就是面向函数式的,之后加入的类组件API颇有些“强扭瓜不甜”的味道。本次,我们将初步了解一下 React Hook 的用法。
React Hook
一、关于React的两套API
很久以前,React大多数情况下都是使用一套API 即类组件API,然而随着React引入了全新的基于函数的钩子 Hook,函数式组件的使用变得更加广泛。
因此,现在任何一个组件既可以用类来写,又可以用钩子即函数来写。
示例代码如下:
1 | // class component |
正如前文所说,尽管两者都可以使用,但是现在官方推荐使用钩子(函数)。
使用函数式的写法即钩子有很多好处:
视觉上:钩子更简洁,代码量更少
原因:钩子 Hooks可以更优雅地实现逻辑复用。开发人员可以把逻辑抽象成钩子 Hooks 以实现代码复用,进一步,更可以将其发布为Library,供React生态社区的其他人使用。
再比如:钩子 Hooks (主要为useEffect) 充当了函数式组件中的“生命周期”的概念,这也大大减少了API
使用上:钩子更灵活,用起来更“轻”,类组件则相对更“重”
🌈屁:更符合React函数式的本质
二、二者差异
1. Class Component
类组件是数据和逻辑的封装。组件的状态和操作方法是封装在一起的。因此在类组件的写法中,相关的数据和操作方法一般需要写在同一个 class 中。
2. Function Component
函数组件是只聚焦于一件事,即返回一个值。如果有多个操作,每个操作应该单独写成一个函数。并且,数据的状态应该与操作方法分离。因此,在React中的函数组件应该只专注于一件事即:返回组件的HTML代码。这种根据输入的数据,只进行单纯的数据计算的函数,称为“纯函数”(在函数式编程中)。
Notes:以上参考了阮一峰老师的Blog。
三、副效应
首先考虑一个问题,函数式组件是纯函数,因此只能进行数据计算,那么诸如生成日志、存储数据、改变应用状态等等不涉及计算的操作就无法被函数组件直接包含,我们把它们称为副效应(side effect)。因为如果函数内部直接包含副效应,它便不再是纯函数了,所以在函数组件内部,我们通过间接的方法来包含副效应。
四、钩子 Hook
简言之:钩子(hook)是React函数组件的副效应的解决方案,用来为函数组件引入副效应。
函数组件只用来返回组件的HTML代码,所有的副效应都通过钩子来引入。
由于副效应很多,所以相应的钩子也有很多。React为一些常见的副效应提供了专用的钩子。
- useState(): 保存状态
- useContext(): 保存上下文
- useRef(): 保存引用
- …
以上的这些钩子都是引入某种特定的副效应,而 useEffect() 是通用的副效应钩子。在找不到对应的钩子时,可以使用它。
五、useEffect() 的用法
1. 基本用法——第一个参数
useEffect() 本身是一个函数,由React提供,在函数组件内部调用即可。
useEffect() 的第一个参数是一个函数,它就是副效应。组件的首次加载到DOM以及以后组件的每次渲染,React都会自动执行该函数。
例如,我们希望组件加载以后,网页标题(document.title)会随之改变。那么,改变标题这个操作就是组件的副效应,可以通过 useEffect() 来实现。
1 | import React, { useEffect } from 'react'; |
2. 基本用法——第二个参数
但是,有些时候,我们不希望 useEffect() 每次渲染都执行,这时候可以使用它的第二个参数。useEffect() 的第二参数是一个数组类型的参数,它可以用于指定副效应函数的依赖项,只有当依赖项发生变化时,组件才会重新渲染。
示例如下:
1 | function Welcome(props) { |
但是,如果第二个参数是一个空数组,就表明副效应函数没有任何依赖项。因此,副效应函数只会在组件加载到DOM后执行一次,此后即使组件重新渲染,也不再执行。
六、useEffect() 的用途
只要是副效应,都可以使用 useEffect() 来引入。它的常见用途有如下几种:
- 获取数据(data fetching)
- 事件监听或订阅(setting up a subscription)
- 改变 DOM(changing the DOM)
- 输出日志(logging)
示例如下(从远程服务器获取数据):
1 | import React, { useState, useEffect } from 'react'; |
在该示例中,useState() 用来生成一个状态变量和设置状态变量的方法以保存/更新获取的数据。useEffecr() 的副效应函数内部有一个异步函数 async 函数,用来从服务器异步获取数据,等拿到数据以后,再用 setData() 更新状态以出发组件重新渲染。
注意:此处的第二个参数为一个空数组,因为在该示例中只需获取数据一次即可。
七、useEffect() 的返回值
副效应是随着组件加载而产生的,那么当组件卸载时,有时也需要清理这些副效应。
useEffect() 允许返回一个函数,在组件卸载时会执行该函数,清理副效应(实际上,由于副效应函数在组件每次渲染时都会执行,因此清理副效应不仅会发生在组件卸载时,而且每次副效应执行前,都会执行一次来清理上一次渲染的副效应)。如果不需要清理副效应,那么 useEffect() 不用返回任何值。
例如,副效应为组件加载时订阅一个事件,那么在组件卸载时,我们需要取消订阅,因此我们需要清理这个副效应。示例如下:
1 | useEffect(() => { |
八、useEffect() 的注意点
使用 useEffect() 时,如果有多个副效应,**应该调用多个 useEffect()**,而不是合并写成一个。
Notes:以上内容借鉴自阮一峰老师的博客。