概述
React Hooks是React16.8新增的特性,这个特性主要是对函数型组件进行增强,让函数组件做类组件可以做的事情。
本文主要是以下内容:
- React Hooks 介绍
- React Hooks 使用
- 自定义 Hook
React Hooks 介绍
- React Hooks 是用来做什么的? 对函数型组件进行增强,让函数型组件可以存储状态,让函数型组件可以拥有处理副作用的能力,让开发者在不使用类组件的情况下实现相同的功能。
什么是副作用? 在组件当中只要不是把数据转换成视图的代码就属于副作用,比如说获取DOM元素、为DOM元素添加事件、设置定时器、发送Ajax请求。类组件中我们在生命周期函数里来处理这些副作用,在函数组件当中我们就要在这些Hooks当中来处理这些副作用了。
- 类组件的不足(Hooks要解决的问题)
- 缺少逻辑复用机制 为了复用逻辑增加无实际渲染效果的组件(例如使用高阶组件或渲染属性),增加了组件层级 显得十分臃肿 增加了调试的难度以及运行效率的降低
- 类组件经常会变得很复杂难以维护 将一组想干的业务逻辑拆分到了多个生命周期函数中(例如组件挂载后做一件什么事,组件更新后做一件什么事,这两件事属于同一个业务逻辑)
在一个生命周期函数中存在多个不相干的业务逻辑
- 类成员方法不能保证this指向的正确性 给一个元素绑定事件时,当事件处理函数中需要更改转态的时候通常需要更正该函数的this指向否则它会指向undefined,通常需要用bind改变this指向,这样的代码也使得类组件变得难以维护,而且代码看起来会比较复杂。
React Hooks 使用
Hooks 意为钩子,React Hooks 就是一堆钩子函数,React 通过这些钩子函数对函数型组件进行增强,不同的钩子函数提供了不同的功能。钩子函数以use进行开头。
useState()
用于为函数组件引入状态
该功能是让函数型组件保存状态。在现有的认知里,一个函数当中的变量在函数执行完成之后就会被释放,所以函数型组件原本是不可以保存数据的,但是有了useState之后就可以实现数据保存。
useState这个方法的内部是使用闭包来保存状态数据的。
使用:
- 接受唯一的参数即状态初始值,初始值可以是任意数据类型。
- 返回值为数组,数组中存储状态值和更改状态值的方法,方法名称约定以set开头,后面加上状态名称。
- 方法可以被多次调用,用以保存不同的状态值。
- 参数可以是一个函数,函数返回什么,初始状态值就是什么,函数只会被调用一次,用在初始值是动态值的情况。
- 设置状态值的方法的参数可以是一个值也可以是一个函数(函数接受的形参是状态值)
- 设置状态值方法的方法本身是异步的,如果需要依赖新值,则需放置在设置状态值方法的回调函数当中
useReducer()
useReducer是另一种让函数组件保存状态的方式
该函数的作用是让函数型组件保存转态。看到reducer会想到redux,useReducer函数的使用方式与redux是及其相似的,状态都被保存在一个特定的地方,组件想要更改状态需要通过dispatch这个方法触发action,action会被reducer函数接受到,reducer函数内部会判断action的类型来决定要对状态进行怎样的处理,最后通过返回值的方式来更新状态。
使用:
useReducer() 相比于 useState() 的好处:
子组件想要修改父组件的数据不需要传递多个修改数据的方法,可以直接将dispatch传递给子组件,子组件通过触发dispatch转入相应action修改数据。
useContext()
在跨组件层级获取数据时简化获取数据的代码。
useContext钩子函数用来简化代码(用createContext()跨组件层级传递数据时简化获取数据时的代码)
使用createContext()跨组件层级传递数据:
useEffect()
让函数型组件拥有处理副作用的能力,类似生命周期函数
- useEffect 执行时机 可以把 useEffect 看做 componentDidMount, componentDidUpdate 和 componentWillUnMount 这三个函数的组合。
- useEffect(() => {}) 相当于componentDidMount, componentDidUpdate
- useEffetc(() => {}, []) 相当于 componentDidMount
- useEffect(() => () => {}) 相当于 componentWillUnMount
- useEffect 使用
- 为window对象添加滚动事件
- 设置定时器让count数值每隔一秒加1
useEffect处理副作用相比传统的类组件有什么优势?
-
按照用途将代码进行分类(将一组相干的业务逻辑归置到了同一个副作用函数中)。
-
简化重复代码,使组件内部代码更加清晰,
- 体现在 useEffect(() => {}) 相当于componentDidMount, componentDidUpdate。
- 数据监测 useEffect(() => {}) 相当于componentDidMount, componentDidUpdate
- 只有指定数据变化时触发 effect
- useEffect 结合异步函数
但是改成 以下写法会报错,因为 useEffect 中的参数函数不能是异步函数,因为useEffect函数要返回清理资源的函数,如果是异步函数就变成了返回是Promise,改变了返回值类型。
那么怎么在useEffect中使用异步函数呢?
在useEffect里面添加函数自执行:
useMemo
useMemo 的行为类似Vue中的计算属性,可以监测某个值的变化,根据变化计算新值,计算出来的值可参与视图渲染。
useMeno 会缓存计算结果,如果监测值没有发生变化,即使组件重新渲染,也不会重新计算,此行为可以有助于避免在每个渲染上进行昂贵的计算。
使用:
memo 方法
性能优化,如果本组件中的数据没有发生变化,阻止组件更新,类似类组件中的 PureComponent 和 shouldComponentUpdate
如下图,当点击改变count值,App组件重新渲染,Foo组件也会被重新渲染
为了Foo组件不被重新渲染,使用memo解决:
useCallback
性能优化,缓存函数,使组件重新渲染时得到相同的函数实例,这样就不会导致里层组件重新渲染。
使用:
useRef()
- 获取DOM元素对象
- 保存数据(跨组件周期)
- 即使组件重新渲染,保存的数据仍然还在(保存一些程序运行的辅助数据)
- 保存的数据更改不会触发组件重新渲染。
自定义 Hook 函数
- 自定义 Hook 是标准的封装和共享逻辑的方式。
- 自定义 Hook 是一个函数,其名称以 use 开头。
- 自定义 Hook 其实就是逻辑和内置 Hook 的组合。
实现组件间逻辑共享:
多次调用同一个自定义Hook,返回的数据不共享,是独立的:
React 路由 Hooks
- react-router-dom 路由提供的钩子函数
这些钩子函数的作用就是用来获取路由信息的,当进入到某个路由组件的时候,这个组件的props里就会多出几个以下几个对象,这些钩子函数的作用就是获取对应对象的信息