react中useRef的使用和渲染机制

 更新时间:2025年11月21日 08:29:30   作者:暴走的皮卡丘  
本文主要介绍了react中useRef的使用和渲染机制,react useRef

前言

刚开始使用react时,由于对react的hook不太了解,导致在使用useState时,出现了闭包的问题,当时搜索解决方法时,发现了useRef这个hook可以很快的解决这个问题。这里用来记录下自己对useRef这个hook的理解。

useRef的渲染机制

先要了解react中useRef和useState的区别,useState是用来管理组件状态的,而useRef是用来管理组件引用的。useState会导致组件重新渲染,而useRef不会。对应上面说的useRef解决闭包问题,其实不是react设置该hook的初衷,useRef这个hook的初衷是用来解决DOM操作问题的。能够解决闭包问题也只是其副作用之一。对于useRef的渲染机制我们可以总结以下几个关键点:

  • useRef在组件首次渲染时创建一个对象 { current: initialValue }
  • 整个组件的生命周期,不会创建新对象,返回的都是首次创建对象的引用
  • 无论如何赋值,都不会导致组件重新渲染(React通过Object.is比较检测不到变化,因此不会触发渲染)

以下是基于useRef的渲染机制的代码示例,组件使用了antd的Button和Card组件,从代码运行中我们可以看出,点击更新Ref值按钮,组件没有重新渲染,但是点击更新State值按钮,组件会重新渲染。

import { Button, Card } from "antd";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";

const DemoRef = () => {
    const ref = useRef<any>(null)
    // 渲染次数
    const renderCountRef = useRef(1);
    const renderValueRef = useRef<any>(0);
    const [renderValue, setRenderValue] = useState<any>(0);

    useEffect(() => {
        renderCountRef.current = renderCountRef.current + 1;
        console.log(`🔄 组件第 ${renderCountRef.current + 1} 次渲染`);
    });

    return <div className="flex w-full">
        <Card title="useRef的渲染机制" className="ml-12px" hoverable={true}>
            <div className="mt-12px max-w-200px text-[#e74c3c] bg-[#fffacd] p-4 text-[18px] font-bold flex w-full flex-row w-400px">
                组件渲染此时:<span className="font-bold">{renderCountRef.current}</span>
            </div>
            <Button onClick={() => {
                renderValueRef.current = renderCountRef.current + 1;
            }} className="mt-12px">更新Ref值</Button>
            <div className="mt-12px font-bold mb-12px">当前Ref值:{renderValueRef.current}(点击虽然新增了,但是组件没有重新渲染,导致此处仍然时老的值)</div>
            <Button onClick={() => {
                setRenderValue((pre: number) => pre + 1);
            }}>更新State值</Button>
            <div className="mt-12px font-bold">当前State值:{renderValue}(点击会触发组件重新渲染,导致此处的值会更新)</div>
        </Card>

    </div>
}

useRef解决的问题

  • 解决闭包问题:useRef能够在闭包函数中访问到最新的状态或属性是因为.current属性的引用不会改变。实际编码中以下两个场景会产生闭包,计时器显示和事件函数监听,下面分享下useRef在这两种场景的应用

    • 计时器中使用最新的状态或属性
    const [duration, setDuration] = useState(0);
    const durationRef = useRef(duration);
    useEffect(() => {
      const interval = setInterval(() => {
        durationRef.current = durationRef.current + 1;
        setDuration(durationRef.current);
      }, 1000);
      return () => clearInterval(interval);
    }, []);
    
    • 事件处理函数中使用最新的状态或属性,此处不再列举代码和说明,因为和计时器的场景类似。
  • react组件中DOM操作:useRef可以用来操作DOM元素,例如获取输入框的值、滚动到指定位置等。 const inputRef = useRef(null); const handleClick = () => { inputRef.current.focus(); };

  • 解决性能问题,方便避免重复创建ref的内容

useRef的好兄弟forwardRef

在日常的开发中,多层组件嵌套是常有的场景,例如父组件中嵌套子组件,子组件中又嵌套孙子组件等。在这种场景下,我们偶尔会需要在父组件中操作子组件的DOM。这时候,如果直接在子组件上使用useRef,会获取不到子组件的DOM并且控制还会报错,原因是react为了保证组件的封装性,默认情况下自定义的组件是不会暴漏其内部DOM节点的ref,具体错误大家可以自己试试。报错提示中会提示我们在子组件中需要使用forwardRef来转发ref。

  • 以下是基于forwardRef的代码示例,从代码运行中我们可以看出,点击设置子组件的年龄为18按钮,子组件的年龄会更新为18。
  • 使用方式:子组件使用forwardRef包裹,需要转发的方式使用useImperativeHandle
import { Button, Card } from "antd";
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";

const LoginForm = forwardRef((props: any, ref: React.ForwardedRef<{ reset: (flag: boolean) => void; }>) => {
    const { name } = props;
    const [formData, setFormData] = useState({
        username: '',
        password: ''
    });
    useImperativeHandle(ref, () => ({
        // 重置表单内容
        reset: () => {
            setFormData({
                username: '',
                password: ''
            })
        },
    }));
    return <div className="mt-12px font-bold">
        <h2>{name}</h2>
        <div>
            <div className="mt-12px">
                用户名:<Input type="text" value={formData.username} onChange={(e) => {
                    setFormData({
                        ...formData,
                        username: e.target.value
                    })
                }} />
            </div>
            <div className="mt-12px">
                密码:<Input type="password" value={formData.password} onChange={(e) => {
                    setFormData({
                        ...formData,
                        password: e.target.value
                    })
                }} />
            </div>
        </div>
    </div>
})
//父组件
  <Card title="useRef和useForwardRef的组合" className="ml-12px" hoverable={true}>
            <Button onClick={() => {
                childRef.current?.reset();
            }} type="primary">重置</Button>
            <Divider></Divider>
            <LoginForm name="登录表单" ref={childRef} />
        </Card>
  • forwardsRef注意点
    • forwardsRef和useRef组合很方便操作子组件的DOM,但是我们尽量避免在父组件中直接操作子组件的DOM,因为这会破坏组件的封装性,导致代码难以维护。
    • forwardsRef和useRef组合通过useImperativeHandle转发的方法,我们可以在父组件中控制子组件的状态,但是如非必要此种也尽量少用,优先用 useState + props 传递状态(如父组件通过 isVisible props 控制子组件弹窗),而非用 ref 调用方法(ref 仅用于 “必须操作 DOM / 内部方法” 的场景)

到此这篇关于react中useRef的使用和渲染机制的文章就介绍到这了,更多相关react useRef使用和渲染内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • React项目中不需要jQuery原因分析

    React项目中不需要jQuery原因分析

    在Web开发的早期,jQuery是一个革命性的库,它极大地简化了DOM操作、事件处理、动画制作以及Ajax请求等任务,React的出现,jQuery在新项目中的必要性开始受到质疑,本文将探讨为什么在React应用中不需要jQuery,感兴趣的朋友可以参考下
    2024-02-02
  • react组件从搭建脚手架到在npm发布的步骤实现

    react组件从搭建脚手架到在npm发布的步骤实现

    这篇文章主要介绍了react组件从搭建脚手架到在npm发布的步骤实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • React实现Excel文件的导出与在线预览功能

    React实现Excel文件的导出与在线预览功能

    这篇文章主要为大家详细介绍了如何利用 React 18 的强大功能,演示如何使用 React 18 编写 Excel 文件的导出与在线预览功能,需要的小伙伴可以参考下
    2023-12-12
  • Ant Design 组件库之步骤条实现

    Ant Design 组件库之步骤条实现

    这篇文章主要为大家介绍了Ant Design组件库之步骤条实现,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 编写简洁React组件的小技巧

    编写简洁React组件的小技巧

    这篇文章主要介绍了编写简洁React组件的小技巧,帮助大家更好的理解和学习使用React,感兴趣的朋友可以了解下
    2021-04-04
  • react-router 重新加回跳转拦截功能详解

    react-router 重新加回跳转拦截功能详解

    这篇文章主要为大家介绍了react-router 重新加回跳转拦截功能详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • React模仿网易云音乐实现一个音乐项目详解流程

    React模仿网易云音乐实现一个音乐项目详解流程

    这篇文章主要介绍了React模仿网易云音乐实现一个音乐项目的详细流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • react封装Dialog弹框的方法

    react封装Dialog弹框的方法

    这篇文章主要为大家详细介绍了react封装Dialog弹框的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 详解对于React结合Antd的Form组件实现登录功能

    详解对于React结合Antd的Form组件实现登录功能

    这篇文章主要介绍了详解对于React结合Antd的Form组件实现登录功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • 详解React-Native全球化多语言切换工具库react-native-i18n

    详解React-Native全球化多语言切换工具库react-native-i18n

    这篇文章主要介绍了详解React-Native全球化语言切换工具库react-native-i18n,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11

最新评论