12-Hooks

7/24/2023

# 一,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是用于在函数式组件中定义状态的。

使用之,如下:

1675328889302

看如下代码,说结果:

1675329351573

# 3,useEffect

effect是副作用的意思。作用:是用来模拟类组件中的生命周期的,不是模拟所有的生命周期,是模拟这三个componentDidMount/componentDidUpdate/componentWillUnmount生命周期。

类似于vue中的watchEffect,vue中的watchEffect会自动依赖依赖,React中的useEffect,需要手动指定依赖。

vue中的watchEffect语法:

watchEffect(() => {}, {
    flush: "sync"
})
1
2
3

语法:

useEffect(() => {
    // fn1中写副作用
    return ()=>{
        // fn2中清除副作用
    }
}, [依赖数组])
1
2
3
4
5
6

在fn1中写一些副作用,在fn2中清除副作用。

useEffect的工作流程(背会):

  • 当没有“依赖数组”这个参数时,初始化只执行fn1,当re-render时,先执行fn2,再执行fn1。当路由切换时,只执行fn2。
  • 当有“依赖数组”这个参数时,但是是一个空数组,初始化只执行fn1,当re-render时,什么也不执行。当路由切换时,只执行fn2。
  • 当有“依赖数组”这个参数时,但是不是一个空数组,初始化只执行fn1。有且仅有当“依赖数组"中的变量发生变化导致rerender时,先执行fn2,再执行fn1()。当路由切换时,只执行fn2。

没有依赖数组时,如下:

1675387653034

fn1类似于类组件中的compoentDidMount,如下:

1675387802368

考虑有依赖数组,但是是一个空数组,如下:

1675387919014

考虑依赖数组中有数据,如下:

1675388166695

1675388289909

依赖可以有多个,如下:

1675388370485

语法总结:

  • 格式: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,它们是彼此不影响,如下:

1675388842691

注意细节:

  • useEffect是用来执行副作用,建议一个useEffect只执行一个副作用。不要在同一个useEffect中同时执行多个副作用。
  • 在函数式组件中,不要把副作用直接暴露在函数体内,一定要使用useEffect进行控制。如下:

1675389244167

实现计数器,每隔一s加1,如下:

1675389538769

# 4,useLayoutEffect

运行机制和useEffect是一样的,区别在于useLayoutEffect执行时候更早。一般在项目中很少用。在一些第三方库中用的比较多。在这个hook中,不能进行ref或dom操作。

代码如下:

1675391016974

# 5,useContext

函数式组件中是没有上下文的,使用useContext,就可以在函数式组件中使用上下文,说白了,就是提供了一个访问上下文的入口。

语法:

const ctx = useContext(上下文对象)
1

代码如下:

1675391456124

除了上面的写法,还有一种写法,使用useContext,如下:

1675391545523

# 6,useRef

回顾昨天讲的ref与ref转化:

  • 在类组件中,如果ref写在DOM元素上,目的是为了获取DOM元素,进而操作DOM元素。
  • 如果ref写在类组件标签上,目的是为了获取组件实例。进而实现组件的通信。
  • 如果ref写在函数组件标签上,会报错。需要使用ref转发,转发到了函数式组件中的JSX中的DOM标签上。进而获取函数式组件中的JSX中的DOM元素。

直接上代码如下:

1675391972941

# 7,useMemo

作用:用于性能优化,用于把一些比较消耗性能的计算进行缓存,类似于vue中的计算属性。

语法:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
1

特点:有且仅有当”依赖数组"中的状态发生变化了,useMemo才会重新执行那个昂贵的计算。

注意:useMemo的第二个参数是依赖数且,也有三种写法,和useEffect一样。

computeExpensiveValue表示昂贵计算。

直接上代码,如下:

1675392418833

如果指定了依赖,只有依赖发变化了,才会重新计算,如下:

1675392486304

1675392552083

# 8,useCallback

作用:用于性能优化,用来缓存一个函数声明。

useCallback也的也不多,官方说,可以使用useMemo去代替useCallback。如下:

1675393167883

语法:

const fn = useCallback(() => {}, [依赖数组])
1

先看如下的代码:

1675392914044

使用useCallback把函数声明缓存起来,如下:

1675393048945

# 9,useReducer

useState是用来定义状态的,如果有很多的状态,通过需要写多个useState。使用useReducer有一个好处,一个useReducer可以代替多个useState。是在React函数式组件中模块Redux的数据流。

useReducer可以一次性定义多个状态。 项目中用的也不多。后面讲redux,还会说这一个数据流。

语法:

const [state, dispath / foreUpdate / setState] = useReducer(reducer, {
    初始值
})
1
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
1
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

效果如下:

1675395069058

# 10,useId

作用:返回一个唯一的标识,在函数式组件的整个运行过程中都是唯一的。

注意:是V18版本新增的。

代码如下:

1675395345857

# 11,useDeferredValue

作用:和防抖类似。和真正的防抖区别在于,这个hook所延迟的时间是不确定,由浏览器自己决定。

直接上代码,如下:

1675395616264

上面发送ajax,对服务器造成很多的压力,需要防抖,有了useDeferredValue这个hook,我们就不需要实现防抖了,如下:

1675395953604

大家自己下查一下。

剩下的没有说的,自己学习。

Last Updated: 2/27/2023, 10:05:39 PM