• React Hook用法详解(6个常见hook)


    1、useState:让函数式组件拥有状态

    用法示例:

    
    import { useState } from 'react'
    const Test = () => {
        const [count, setCount] = useState(0);
        return (
            <>
                

    点击了{count}次

    ); } export default Test
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    PS:class组件中this.setState更新是state是合并, useState中setState是替换。例如:

    
    import { useState } from 'react'
    const Test = () => {
        const [counts, setCounts] = useState({
            num1: 0,
            num2: 0
        });
        return (
            <>
                

    num1:{counts.num1}

    num2:{counts.num2}

    ); } export default Test
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    可以看到useState中setState是替换,不会合并,正确更新:

    import { useState } from 'react'
    const Test = () => {
        const [counts, setCounts] = useState({
            num1: 0,
            num2: 0
        });
        return (
            <>
                

    num1:{counts.num1}

    num2:{counts.num2}

    ); } export default Test
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2、useEffect:副作用,取代生命周期

    用法示例,在class组件中如果需要在组件挂载后和数据更新后做同一件事,我们会这样做:

    componentDidMount() {
        
    }
    componentDidUpdate() {
        
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以看出来,如果逻辑复杂后,代码看起来不优雅,且容易造成逻辑混乱,而使用useEffect:

    useEffect(() => {
        
    });
    
    
    • 1
    • 2
    • 3
    • 4

    此刻已经看到了useEffect的基本用法,除此之外,他还可以绑定触发更新的依赖状态,默认是状态中任何数据发生变化副作用都会执行,如:

    import { useState, useEffect } from 'react'
    const Test = () => {
        const [count1, setCount1] = useState(0);
        const [count2, setCount2] = useState(0);
        useEffect(() => {
            console.log('useEffect触发了')
        });
        return (
            <>
                

    count1:{count1}

    count2:{count2}

    ); } export default Test
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    将上述代码useEffect第二个参数传入需要绑定的状态,可绑定多个:

    
    useEffect(() => {
        console.log('useEffect触发了')
    }, [count1]);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看到,只有绑定的count1发生变化才会触发,如果传空数组则任何状态发生变化都不会触发,此时useEffect的作用就类似class组件中的componentDidMount,所以发送请求通常也会在此执行。

    清理副作用
    在上面的操作中都不用清理的副作用,然而,有些副作用是需要去清理的,不清理会造成异常甚至内存泄漏,比如开启定时器,如果不清理,则会多次开启,从上面可以看到useEffect的第一个参数是一个回调函数,可以在回调函数中再返回一个函数,该函数可以在状态更新后第一个回调函数执行之前调用,具体实现:

    seEffect(() => {
        
        return () => {
            
        }
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、useContext:跨组件共享数据

    React.createContext();创建一个TestContext对象
    TestContext.Provider包裹子组件
    数据放在的value中
    子组件中通过useContext(TestContext)获取值

    import React, { useContext, useState } from 'react';
    const TestContext = React.createContext();
    const Parent = () => {
        const [value, setValue] = useState(0);
        return (
            
    {(() => console.log("Parent-render"))()}
    ); } const Child1 = () => { const value = useContext(TestContext); return (
    {(() => console.log('Child1-render'))()}

    Child1-value: {value}

    ); } const Child2 = () => { return (
    {(() => console.log('Child2-render'))()}

    Child2

    ); } export default Parent
    • 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

    至此数据实现共享了,但是可以看到在TestContext中的共享数据只要发生变化,子组件都会重新渲染,Child2并没有绑定数据,不希望他做无意义的渲染,可以使用React.memo解决,实现:

    const Child2 = React.memo(() => {
        return (
            
    {(() => console.log('Child2-render'))()}

    Child2

    ); });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4、useCallback:性能优化

    
    const handleClick = useCallback(()=> {
        
    }, [value]);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    useCallback返回的是一个 memoized(缓存)函数,在依赖不变的情况下,多次定义的时候,返回的值是相同的,他的实现原理是当使用一组参数初次调用函数时,会缓存参数和计算结果,当再次使用相同的参数调用该函数时,会直接返回相应的缓存结果。

    优化性能例子:

    import React, { useState, useCallback, memo } from 'react';
    const Parent = () => {
        const [value1, setValue1] = useState(0);
        const [value2, setValue2] = useState(0);
        const handleClick1 = useCallback(()=> {
            setValue1(value1 + 1);
        }, [value1]);
        const handleClick2 = useCallback(()=> {
            setValue2(value2 + 1);
        }, [value2]);
        return (
            <>
                {(() => console.log("Parent-render"))()}
                

    {value1}

    {value2}

    ); } const Child1 = memo(props => { return (
    {(() => console.log("Child1-render"))()}
    ); }); const Child2 = memo(props => { return (
    {(() => console.log("Child2-render"))()}
    ); }); export default Parent
    • 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

    useCallback返回的是一个memoized回调函数,仅在其中绑定的一个依赖项变化后才更改可防止不必要的渲染,在跨组件共享数据中举例的事件是在父组件中点击触发,而现在是使用状态提升,在父组件中传递方法供子组件调用,每次render时函数也会变化,导致子组件重新渲染,上面例子useCallback将函数进行包裹,依赖值未发生变化时会返回缓存的函数,配合React.memo即可优化无意义的渲染。

    5、useMemo:性能优化

    语法:

    
    useMemo(() => {
        
    },[value]);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    先看一个例子:

    import React, { useState } from 'react'
    const Test = ()=> {
        const [value, setValue] = useState(0);
        const [count, setCount] = useState(1);
        const getDoubleCount = () => {
            console.log('getDoubleCount进行计算了');
            return count * 2;
        };
        return (
            

    value: {value}

    doubleCount: {getDoubleCount()}

    ) } export default Test
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    可以看到getDoubleCount依赖的是count,但value发生变化它也重新进行了计算渲染,现在只需要将getDoubleCount使用useMemo进行包裹,如下:

    import React, { useState, useMemo } from 'react'
    const Test = ()=> {
        const [value, setValue] = useState(0);
        const [count, setCount] = useState(1);
        const getDoubleCount = useMemo(() => {
            console.log('getDoubleCount进行计算了');
            return count * 2;
        },[count]);
        return (
            

    value: {value}

    doubleCount: {getDoubleCount}

    ) } export default Test
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    现在getDoubleCount只有依赖的count发生变化时才会重新计算渲染。

    useMemo和useCallback的共同点:

    接收的参数都是一样的,第一个是回调函数,第二个是依赖的数据
    它们都是当依赖的数据发生变化时才会重新计算结果,起到了缓存作用

    useMemo和useCallback的区别:

    useMemo计算结果是return回来的值,通常用于缓存计算结果的值
    useCallback计算结果是一个函数,通常用于缓存函数

    6、useRef

    用法:例如要实现点击button按钮使input输入框获得焦点:

    import React, { useRef } from 'react';
    const Test = () => {
        const inputEl = useRef();
        return (
            <>
                
                
            
        );
    }
    export default Test
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这样看起来非常像React.createRef(),将上面代码中的useRef()改成React.createRef()也能实现同样的效果,那为什么要设计一个新的hook?难道只是为了加上use,统一hook规范?
    事实上,它们确实不一样。

    官网的说明如下:

    useRef returns a mutable ref object whose .current property is initialized to the passed
    argument (initialValue). The returned object will persist for the full lifetime of the component.
    
    
    
    • 1
    • 2
    • 3
    • 4

    简单来说,useRef就像一个储物箱,你可以随意存放任何东西,再次渲染时它会去储物箱找,createRef每次渲染都会返回一个新的引用,而useRef每次都会返回相同的引用。

    摘自:https://www.cnblogs.com/hymenhan/archive/2021/04/28/14711516.html

  • 相关阅读:
    【CV知识点汇总与解析】| 正则化篇
    『进阶之路』- 揭开ThreadLocal神秘面纱
    【前端学习】—多种方式实现数组拍平(十一)
    MQTT,如何在SpringBoot中使用MQTT实现消息的订阅和发布
    玩全栈,做自己喜欢做的事,写自己喜欢写的代码
    如何在 pyqt 中自定义工具提示 ToolTip
    先行进位加法器
    GAN实战笔记——第三章第一个GAN模型:生成手写数字
    牛客网刷题-(3)
    如何学习存储性系统
  • 原文地址:https://blog.csdn.net/ling_76539446/article/details/127666431