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使用和渲染内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • create-react-app构建项目慢的解决方法

    create-react-app构建项目慢的解决方法

    这篇文章主要介绍了create-react-app构建项目慢的解决方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • React中井字棋游戏的实现示例

    React中井字棋游戏的实现示例

    本文主要介绍了React中井字棋游戏的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • react项目中使用react-dnd实现列表的拖拽排序功能

    react项目中使用react-dnd实现列表的拖拽排序功能

    这篇文章主要介绍了react项目中使用react-dnd实现列表的拖拽排序,本文结合实例代码讲解react-dnd是如何实现,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • React Antd中如何设置表单只输入数字

    React Antd中如何设置表单只输入数字

    这篇文章主要介绍了React Antd中如何设置表单只输入数字问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • React的Props、生命周期详解

    React的Props、生命周期详解

    “Props” 是 React 中用于传递数据给组件的一种机制,通常作为组件的参数进行传递,在 React 中,props 是只读的,意味着一旦将数据传递给组件的 props,组件就不能直接修改这些 props 的值,这篇文章主要介绍了React的Props、生命周期,需要的朋友可以参考下
    2024-06-06
  • React使用useLayoutEffect解决操作DOM页面闪烁问题的方法

    React使用useLayoutEffect解决操作DOM页面闪烁问题的方法

    本文介绍了useLayoutEffect解决操作DOM页面闪烁问题,useLayoutEffect是React中用于处理副作用的Hook之一,它与 useEffect功能相似,但执行时机有着显著差异,useLayoutEffect会在所有的DOM变更之后同步执行,这一特性使其特别适合用于需要基于最新DOM结构进行操作的场景
    2025-12-12
  • react-native组件中NavigatorIOS和ListView结合使用的方法

    react-native组件中NavigatorIOS和ListView结合使用的方法

    这篇文章主要给大家介绍了关于react-native组件中NavigatorIOS和ListView结合使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-09-09
  • React中嵌入多个组件的多种方式详解

    React中嵌入多个组件的多种方式详解

    在 React 中,嵌入和使用多个组件是构建用户界面的核心机制,React 鼓励组件化开发,允许将 UI 拆分为独立、可复用的部分,以下是实现多个组件嵌套和使用的常见方法,需要的朋友可以参考下
    2025-08-08
  • React新文档切记不要滥用Ref

    React新文档切记不要滥用Ref

    这篇文章主要为大家介绍了React新文档滥用Ref出现的问题详解,以及如何正确的使用Ref,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-07-07
  • 使用react-dnd编写一个可拖拽排列的list

    使用react-dnd编写一个可拖拽排列的list

    这篇文章主要为大家详细介绍了如何使用react-dnd编写一个可拖拽排列的list,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-03-03

最新评论