12-Hooks
# 一,Hooks
# 1,什么是hook
hook是React官方提供的API,10多个。是V16.8中新增的。已经出来几年了。类似于Vue3中的组合式API。
- 作用:用于在函数式组件中模拟出类组件的功能。如:state,生命周期,ref,上下文...
- 价值:有了hook,我们就可以不再使用类组件。官方说了,并不是要淘汰类组件。hook不能出现在类组件。
- 哪些:useState, useEffect, useLayoutEffect,useContext, userReducer, useRef, useMemo, useCallback..
- 开源hook: react-use, ahooks...
# 2,useState
useState是用于在函数式组件中定义状态的。
使用之,如下:
看如下代码,说结果:
# 3,useEffect
effect是副作用的意思。作用:是用来模拟类组件中的生命周期的,不是模拟所有的生命周期,是模拟这三个componentDidMount/componentDidUpdate/componentWillUnmount生命周期。
类似于vue中的watchEffect,vue中的watchEffect会自动依赖依赖,React中的useEffect,需要手动指定依赖。
vue中的watchEffect语法:
watchEffect(() => {}, {
flush: "sync"
})
2
3
语法:
useEffect(() => {
// fn1中写副作用
return ()=>{
// fn2中清除副作用
}
}, [依赖数组])
2
3
4
5
6
在fn1中写一些副作用,在fn2中清除副作用。
useEffect的工作流程(背会):
- 当没有“依赖数组”这个参数时,初始化只执行fn1,当re-render时,先执行fn2,再执行fn1。当路由切换时,只执行fn2。
- 当有“依赖数组”这个参数时,但是是一个空数组,初始化只执行fn1,当re-render时,什么也不执行。当路由切换时,只执行fn2。
- 当有“依赖数组”这个参数时,但是不是一个空数组,初始化只执行fn1。有且仅有当“依赖数组"中的变量发生变化导致rerender时,先执行fn2,再执行fn1()。当路由切换时,只执行fn2。
没有依赖数组时,如下:
fn1类似于类组件中的compoentDidMount,如下:
考虑有依赖数组,但是是一个空数组,如下:
考虑依赖数组中有数据,如下:
依赖可以有多个,如下:
语法总结:
- 格式:useEffect(()=>{fn1, return fn2}, [依赖数组])
- 情况一:没有依赖数组,初始化时,执行fn1,rerender时,先fn2,再fn1。路由切换只执行fn2。
- 情况二:有依赖数组,是一个空数组。初始化时,执行fn1,rerender时,fn2和fn1都不执行。路由切换只执行fn2。
- 情况三:有依赖数组,依赖数组中有状态。初始化时,执行fn1,只有依赖数组中的状态变化了,rerender时,先fn2,再fn1。路由切换只执行fn2。
- fn1相当于类组件中的componentDidMount
- fn2相当于类组件中的componentWillUnMount
- 依赖数组,相当于类组件中的componentDidUpdate
在一个函数式组件中,可以书写多个useEffect,多个useEffect,它们是彼此不影响,如下:
注意细节:
- useEffect是用来执行副作用,建议一个useEffect只执行一个副作用。不要在同一个useEffect中同时执行多个副作用。
- 在函数式组件中,不要把副作用直接暴露在函数体内,一定要使用useEffect进行控制。如下:
实现计数器,每隔一s加1,如下:
# 4,useLayoutEffect
运行机制和useEffect是一样的,区别在于useLayoutEffect执行时候更早。一般在项目中很少用。在一些第三方库中用的比较多。在这个hook中,不能进行ref或dom操作。
代码如下:
# 5,useContext
函数式组件中是没有上下文的,使用useContext,就可以在函数式组件中使用上下文,说白了,就是提供了一个访问上下文的入口。
语法:
const ctx = useContext(上下文对象)
代码如下:
除了上面的写法,还有一种写法,使用useContext,如下:
# 6,useRef
回顾昨天讲的ref与ref转化:
- 在类组件中,如果ref写在DOM元素上,目的是为了获取DOM元素,进而操作DOM元素。
- 如果ref写在类组件标签上,目的是为了获取组件实例。进而实现组件的通信。
- 如果ref写在函数组件标签上,会报错。需要使用ref转发,转发到了函数式组件中的JSX中的DOM标签上。进而获取函数式组件中的JSX中的DOM元素。
直接上代码如下:
# 7,useMemo
作用:用于性能优化,用于把一些比较消耗性能的计算进行缓存,类似于vue中的计算属性。
语法:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
特点:有且仅有当”依赖数组"中的状态发生变化了,useMemo才会重新执行那个昂贵的计算。
注意:useMemo的第二个参数是依赖数且,也有三种写法,和useEffect一样。
computeExpensiveValue表示昂贵计算。
直接上代码,如下:
如果指定了依赖,只有依赖发变化了,才会重新计算,如下:
# 8,useCallback
作用:用于性能优化,用来缓存一个函数声明。
useCallback也的也不多,官方说,可以使用useMemo去代替useCallback。如下:
语法:
const fn = useCallback(() => {}, [依赖数组])
先看如下的代码:
使用useCallback把函数声明缓存起来,如下:
# 9,useReducer
useState是用来定义状态的,如果有很多的状态,通过需要写多个useState。使用useReducer有一个好处,一个useReducer可以代替多个useState。是在React函数式组件中模块Redux的数据流。
useReducer可以一次性定义多个状态。 项目中用的也不多。后面讲redux,还会说这一个数据流。
语法:
const [state, dispath / foreUpdate / setState] = useReducer(reducer, {
初始值
})
2
3
直接上代码如下:
import { useState, useEffect, useReducer } from "react"
// state表示状态,action表示信号,根据不同的信号,就可以针对性地修改状态
// reducer 是管理员的意思,要修改状态必须通过reducer
// 管理员根据信号进行状态的修改
// 修改状态的流程:1)对于state进行深copy 2)修改更新state 3)返回修改后的state
const reducer = (state, action) => {
let newState = JSON.parse(JSON.stringify(state)); // 1)对于state进行深copy
// 根据信号更新state
switch (action.type) { // action是一个对象,对象中有一个type,不同的type表示不同的信号
case "NUM_ADD":
newState.num += 1
break;
case "NUM_SUB":
newState.num -= 1
break;
}
return newState;
}
// 定义初始值
const initState = {
num: 1,
list: ["a", "b"],
falg: true
}
const A = props => {
// dispatch 是用来派发一个action,管理员就可以收到action
const [state, dispatch] = useReducer(reducer, initState);
return (
<div>
<h2 >函数组件</h2>
<h3>{state.num}</h3>
<h3>{state.list}</h3>
<button onClick={() => dispatch({ type: "NUM_ADD" })}>+1</button>
<button onClick={() => dispatch({ type: "NUM_SUB" })}>-1</button>
</div>
)
}
export default A
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
效果如下:
# 10,useId
作用:返回一个唯一的标识,在函数式组件的整个运行过程中都是唯一的。
注意:是V18版本新增的。
代码如下:
# 11,useDeferredValue
作用:和防抖类似。和真正的防抖区别在于,这个hook所延迟的时间是不确定,由浏览器自己决定。
直接上代码,如下:
上面发送ajax,对服务器造成很多的压力,需要防抖,有了useDeferredValue这个hook,我们就不需要实现防抖了,如下:
大家自己下查一下。
剩下的没有说的,自己学习。