React Hooks

463 阅读7分钟

概述

React Hooks是React16.8新增的特性,这个特性主要是对函数型组件进行增强,让函数组件做类组件可以做的事情。

本文主要是以下内容:

  1. React Hooks 介绍
  2. React Hooks 使用
  3. 自定义 Hook

React Hooks 介绍

  1. React Hooks 是用来做什么的? 对函数型组件进行增强,让函数型组件可以存储状态,让函数型组件可以拥有处理副作用的能力,让开发者在不使用类组件的情况下实现相同的功能。

什么是副作用? 在组件当中只要不是把数据转换成视图的代码就属于副作用,比如说获取DOM元素、为DOM元素添加事件、设置定时器、发送Ajax请求。类组件中我们在生命周期函数里来处理这些副作用,在函数组件当中我们就要在这些Hooks当中来处理这些副作用了。

  1. 类组件的不足(Hooks要解决的问题)
  • 缺少逻辑复用机制 为了复用逻辑增加无实际渲染效果的组件(例如使用高阶组件或渲染属性),增加了组件层级 显得十分臃肿 增加了调试的难度以及运行效率的降低
  • 类组件经常会变得很复杂难以维护 将一组想干的业务逻辑拆分到了多个生命周期函数中(例如组件挂载后做一件什么事,组件更新后做一件什么事,这两件事属于同一个业务逻辑)

在一个生命周期函数中存在多个不相干的业务逻辑

  • 类成员方法不能保证this指向的正确性 给一个元素绑定事件时,当事件处理函数中需要更改转态的时候通常需要更正该函数的this指向否则它会指向undefined,通常需要用bind改变this指向,这样的代码也使得类组件变得难以维护,而且代码看起来会比较复杂。

React Hooks 使用

Hooks 意为钩子,React Hooks 就是一堆钩子函数,React 通过这些钩子函数对函数型组件进行增强,不同的钩子函数提供了不同的功能。钩子函数以use进行开头。

useState()

用于为函数组件引入状态

该功能是让函数型组件保存状态。在现有的认知里,一个函数当中的变量在函数执行完成之后就会被释放,所以函数型组件原本是不可以保存数据的,但是有了useState之后就可以实现数据保存。

useState这个方法的内部是使用闭包来保存状态数据的。

使用:

image.png

  1. 接受唯一的参数即状态初始值,初始值可以是任意数据类型。
  2. 返回值为数组,数组中存储状态值和更改状态值的方法,方法名称约定以set开头,后面加上状态名称。
  3. 方法可以被多次调用,用以保存不同的状态值。
  4. 参数可以是一个函数,函数返回什么,初始状态值就是什么,函数只会被调用一次,用在初始值是动态值的情况。
  5. 设置状态值的方法的参数可以是一个值也可以是一个函数(函数接受的形参是状态值)
  6. 设置状态值方法的方法本身是异步的,如果需要依赖新值,则需放置在设置状态值方法的回调函数当中

useReducer()

useReducer是另一种让函数组件保存状态的方式

该函数的作用是让函数型组件保存转态。看到reducer会想到redux,useReducer函数的使用方式与redux是及其相似的,状态都被保存在一个特定的地方,组件想要更改状态需要通过dispatch这个方法触发action,action会被reducer函数接受到,reducer函数内部会判断action的类型来决定要对状态进行怎样的处理,最后通过返回值的方式来更新状态。

使用:

image.png

useReducer() 相比于 useState() 的好处

子组件想要修改父组件的数据不需要传递多个修改数据的方法,可以直接将dispatch传递给子组件,子组件通过触发dispatch转入相应action修改数据。

useContext()

在跨组件层级获取数据时简化获取数据的代码。

useContext钩子函数用来简化代码(用createContext()跨组件层级传递数据时简化获取数据时的代码)

使用createContext()跨组件层级传递数据:

image.png

useEffect()

让函数型组件拥有处理副作用的能力,类似生命周期函数

  1. useEffect 执行时机 可以把 useEffect 看做 componentDidMount, componentDidUpdate 和 componentWillUnMount 这三个函数的组合。
  • useEffect(() => {}) 相当于componentDidMount, componentDidUpdate
  • useEffetc(() => {}, []) 相当于 componentDidMount
  • useEffect(() => () => {}) 相当于 componentWillUnMount
  1. useEffect 使用
  • 为window对象添加滚动事件

image.png

  • 设置定时器让count数值每隔一秒加1

image.png

useEffect处理副作用相比传统的类组件有什么优势?

  • 按照用途将代码进行分类(将一组相干的业务逻辑归置到了同一个副作用函数中)。

  • 简化重复代码,使组件内部代码更加清晰,

    • 体现在 useEffect(() => {}) 相当于componentDidMount, componentDidUpdate。
  1. 数据监测 useEffect(() => {}) 相当于componentDidMount, componentDidUpdate

image.png

  1. 只有指定数据变化时触发 effect

image.png

  1. useEffect 结合异步函数

image.png

但是改成 以下写法会报错,因为 useEffect 中的参数函数不能是异步函数,因为useEffect函数要返回清理资源的函数,如果是异步函数就变成了返回是Promise,改变了返回值类型。

image.png

那么怎么在useEffect中使用异步函数呢?

在useEffect里面添加函数自执行:

image.png

useMemo

useMemo 的行为类似Vue中的计算属性,可以监测某个值的变化,根据变化计算新值,计算出来的值可参与视图渲染。

useMeno 会缓存计算结果,如果监测值没有发生变化,即使组件重新渲染,也不会重新计算,此行为可以有助于避免在每个渲染上进行昂贵的计算。

使用:

image.png

memo 方法

性能优化,如果本组件中的数据没有发生变化,阻止组件更新,类似类组件中的 PureComponent 和 shouldComponentUpdate

如下图,当点击改变count值,App组件重新渲染,Foo组件也会被重新渲染

image.png

为了Foo组件不被重新渲染,使用memo解决:

image.png

useCallback

性能优化,缓存函数,使组件重新渲染时得到相同的函数实例,这样就不会导致里层组件重新渲染。

使用:

image.png

useRef()

  1. 获取DOM元素对象

image.png

  1. 保存数据(跨组件周期)
  • 即使组件重新渲染,保存的数据仍然还在(保存一些程序运行的辅助数据)
  • 保存的数据更改不会触发组件重新渲染。

image.png

自定义 Hook 函数

  • 自定义 Hook 是标准的封装和共享逻辑的方式。
  • 自定义 Hook 是一个函数,其名称以 use 开头。
  • 自定义 Hook 其实就是逻辑和内置 Hook 的组合。

实现组件间逻辑共享

image.png

多次调用同一个自定义Hook,返回的数据不共享,是独立的:

image.png

React 路由 Hooks

  • react-router-dom 路由提供的钩子函数

image.png

这些钩子函数的作用就是用来获取路由信息的,当进入到某个路由组件的时候,这个组件的props里就会多出几个以下几个对象,这些钩子函数的作用就是获取对应对象的信息

image.png