• React@16.x(29)useRef


    目前来说,因为函数组件每次触发更新时,都会重新运行。无法像类组件一样让一些内容保持不变。
    所以才出现了各种 HOOK 函数:useStateuseCallbackuseMemo 等来辅助实现和类组件相似的功能。

    useRef 也是这样的目的。

    1,介绍

    之前的文章中介绍了 ref,用于获取组件或真实DOM元素的引用。

    useRef 作用相同,不过可以在函数组件中使用。同时它会返回对象的固定引用。

    换句话说,当函数组件重新运行时,useRef() 前后2次返回的对象引用地址相同。
    一个节点(React元素)对应一个唯一的对象。

    React.createRef() 使用举例:

    import React, { useState } from "react";
    
    export default function App() {
        const refInput = React.createRef();
        return (
            <>
                <input ref={refInput}></input>
                <button
                    onClick={() => {
                        console.log(refInput.current.value);
                    }}
                >
                    获取 inputRef
                </button>
            </>
        );
    }
    

    换成 useRef仅需要替换一行代码:

    const refInput = React.createRef();
    // 替换为
    const refInput = useRef();
    

    2,和 React.createRef() 的区别

    上面的代码中,看起来只是一行代码的区别,但本质上处理逻辑不同。

    • React.createRef(),如果 ref 的值发生变动(函数组件重新渲染),则将旧值设为 null
    • useRef(),函数组件重新渲染多次时,所有返回的对象的引用地址相同。

    验证下:
    简单修改下,将每次更新后的新 ref 放到 window 对象中对比下:

    import React, { useRef, useState } from "react";
    
    window.arr = [];
    export default function App() {
        const refInput = React.createRef();
        window.arr.push(refInput);
        const [n, setN] = useState(); // 只是为了重新渲染组件。
        return (
            <>
                <input
                    ref={refInput}
                    type="text"
                    value={n}
                    onChange={(e) => {
                        setN(e.target.value);
                    }}
                />
            </>
        );
    }
    

    多次改变 input.value 时,检查 window.arr

    在这里插入图片描述

    替换为 const refInput = useRef();

    在这里插入图片描述

    3,计时器的问题

    在之前介绍 useEffect 时,提到了下面的写法是有问题的。

    因为 useEffect 只会执行一次,所以在计时器中通过闭包获取的状态变量 n 永远都是10,

    export default function App() {
        const [n, setN] = useState(10);
        useEffect(() => {
            const timer = setInterval(() => {
                setN(n - 1);
            }, 1000);
            return () => {
                clearInterval(timer);
            };
        }, []);
        return <div>{n}</div>;
    }
    

    该问题,通过将依赖项 n 传入即可。但会引起另一个问题:
    每次函数重新运行,都会再次执行 useEffect,开启计时器又销毁计时器,开销很大。

    export default function App() {
        const [n, setN] = useState(10);
        useEffect(() => {
            const timer = setInterval(() => {
                setN(n - 1);
            }, 1000);
            return () => {
                clearInterval(timer);
            };
        }, [n]);
        return <div>{n}</div>;
    }
    

    所以,可通过 useRef 来将函数重新运行后的新值传递给计时器,同时 useEffect 也只会运行一次,开启一次计时器!

    注意,useRef() 返回的虽然是同一个对象,但 setN 修改的是它的 current 属性。所以计时器每次获取的都是新值。

    export default function App() {
        const [n, setN] = useState(10);
        const refN = useRef(n);
        useEffect(() => {
            const timer = setInterval(() => {
                setN(--refN.current); // 修改的是 n
            }, 1000);
            return () => {
                clearInterval(timer);
            };
        }, []);
        return <div>{n}</div>;
    }
    

    以上。

  • 相关阅读:
    使用定时器按键扫描数码管制作一个可存储数据的秒表
    圆梦字节之后,我收集整理了这份“2021秋招常见Java面试题汇总”
    566页19万字区级一网通办政务服务应用平台建设项目方案书
    用Blender制作YOLO目标检测器训练数据
    API接口中大数据在淘宝中的应用【获取商品详情接口,商品价格,SKU数据,订单……】
    静电监控系统的作用在哪
    java毕业设计电影网站系统设计Mybatis+系统+数据库+调试部署
    要学很多数学吗 - 给要入行机器学习的朋友们的建议
    Sulfo-CY5 MAL生物分子定量和定位Cyanine5 MAL星戈瑞
    华为机试 - 字符串重新排列
  • 原文地址:https://blog.csdn.net/qq_40147756/article/details/139583456