React19源码系列之Hooks(useRef)

在上一篇文章中,介绍了useId的使用及其原理,这次来看一下useRef的使用和它的原理。

useRef的使用

https://zh-hans.react.dev/reference/react/useRef

1、 引用 DOM 元素

useRef 还可以用于引用 DOM 元素,以便在组件中直接操作这些元素。例如,获取输入框的值

js 复制代码
import React, { useRef } from 'react';

function InputExample() {
    const inputRef = useRef(null);

    const handleClick = () => {
        console.log('输入框的值:', inputRef.current.value);
    };

    return (
        <div>
            <input type="text" ref={inputRef} />
            <button onClick={handleClick}>获取输入框的值</button>
        </div>
    );
}

export default InputExample;

2、 存储可变变量

在组件中,有时候需要存储一些变量,这些变量的变化不希望触发组件的重新渲染。例如,在一个定时器中记录当前的计数:

js 复制代码
import React, { useRef, useEffect } from 'react';

function Timer() {
    const countRef = useRef(0);

    useEffect(() => {
        const intervalId = setInterval(() => {
            countRef.current++;
            console.log('Count:', countRef.current);
        }, 1000);

        return () => clearInterval(intervalId);
    }, []);

    return <div>定时器正在运行...</div>;
}

export default Timer;

3、ref传给子组件,父组件中调用子组件方法

js 复制代码
import React, { useRef, useImperativeHandle } from 'react';

const ChildFunctionalComponent = (props) => {
   const { ref } = props
    const internalRef = useRef(null);

    useImperativeHandle(ref, () => ({
        focus: () => {
            internalRef.current.focus();
        }
    }));

    return <input type="text" ref={internalRef} />;
};

function ParentFunctionalComponent() {
    const childRef = useRef(null);

    const handleClick = () => {
        // 调用子组件暴露的方法
        childRef.current.focus();
    };

    return (
        <div>
            <ChildFunctionalComponent ref={childRef} />
            <button onClick={handleClick}>聚焦子组件输入框</button>
        </div>
    );
}

export default ParentFunctionalComponent;

useRef源码

useRef 是 React 提供的一个内置 Hook,它返回一个可变的 ref 对象,该对象具有一个 current 属性,初始值为传入的 initialValue。这个 ref 对象在组件的整个生命周期内保持不变,常用于在组件中存储不需要触发重新渲染的变量,或者引用 DOM 元素。

js 复制代码
function useRef<T>(initialValue: T): {current: T} {
  // resolveDispatcher 是一个内部函数,其作用是获取当前的 React 调度器。调度器负责管理 React 的渲染和更新过程,不同的环境(如开发环境、生产环境、服务器端渲染等)可能使用不同的调度器。
  const dispatcher = resolveDispatcher();
  // 调用获取到的调度器的 useRef 方法,并将 initialValue 作为参数传递给它。
  return dispatcher.useRef(initialValue);
}

mountRef 在函数组件初始化时遇到useRef触发

mountRef 函数是 React 内部用于在组件挂载阶段创建和初始化 ref 对象的函数。在 React 中,ref 是一种用于访问 DOM 节点或组件实例的方式,它提供了一种在函数式组件中存储和访问可变值的方法。

mountRef 函数的主要作用是创建一个 ref 对象,并将其存储在当前正在处理的钩子(hook)的 memoizedState 中,以便在组件的整个生命周期内保持该 ref 对象的引用。

函数数参数含义:

  • initialValue:类型为 T,是 ref 对象的初始值。这个值会被赋值给 ref 对象的 current 属性。
js 复制代码
function mountRef<T>(initialValue: T): {current: T} {
  // mountWorkInProgressHook 是 React 内部的一个函数,用于在当前正在处理的 Fiber 节点上创建一个新的钩子实例。每个钩子实例都有自己的状态和相关信息,存储在 hook 对象中。
  const hook = mountWorkInProgressHook();
  const ref = {current: initialValue};
  // 存储 ref 对象到钩子的 memoizedState 中
  hook.memoizedState = ref;
  // 返回 ref 对象
  return ref;
}

updateRef在函数组件更新时遇到useRef触发

updateRef 函数是 React 内部用于在组件更新阶段处理 ref 的函数。在 React 中,ref 提供了一种访问 DOM 节点或组件实例的方式,并且在组件的整个生命周期内保持引用的稳定性。
updateRef 函数的主要作用是在组件更新时,从当前正在处理的钩子(hook)的 memoizedState 中获取之前存储的 ref 对象并返回,确保在组件更新过程中使用的是同一个 ref 对象。

函数参数含义:

  • initialValue:类型为 T,理论上是 ref 对象的初始值,但在 updateRef 函数中,该参数并没有实际被使用,因为在更新阶段,ref 对象已经在挂载阶段创建好了,这里只是获取之前创建的 ref 对象。
js 复制代码
function updateRef<T>(initialValue: T): {current: T} {
  // updateWorkInProgressHook 是 React 内部的一个函数,用于在当前正在处理的 Fiber 节点上获取当前的钩子实例。
  const hook = updateWorkInProgressHook();
  // 返回存储在钩子中的 ref 对象
  return hook.memoizedState;
}

mountWorkInProgressHook函数和updateWorkInProgressHook函数的具体实现,可看上一篇useId的分析

相关推荐
研☆香11 小时前
jQuery + Swiper.js 实现动态渲染轮播图
前端·javascript·jquery
特立独行的猫A15 小时前
仓颉版 Tauri:基于轻量化线程与纯血鸿蒙架构的下一代 Web 混合开发利器
javascript
李游Leo16 小时前
HarmonyOS 7 ArkTS + ohpm:三方 JS 库副作用隔离与 API 26 升级回归【鸿蒙心迹】
javascript·回归·harmonyos
你挚爱的强哥16 小时前
【sgKeyboard】自定义组件:虚拟键盘
前端·javascript·计算机外设
锅里游的鱼吖16 小时前
echarts自定义折线图
前端·javascript·数据库
何何____17 小时前
Vue 全局事件总线详解
前端·javascript·vue.js
u01110267518 小时前
工具页案例图如何编写 alt让图片说明与处理场景对应
java·前端·javascript·图像处理·人工智能·算法·ai作画
事圆则缓18 小时前
Flutter 状态管理框架对比(五):GetX 的响应式、依赖注册与生命周期
前端·javascript·flutter
凌云志.19 小时前
【protobuf】proto3语法详解&通讯录2.x实现
java·前端·javascript
ss27319 小时前
AI全栈实战 | 2.1-03 JavaScript 核心:单线程+事件循环为什么不等同于多线程,原型链 vs Java 继承
java·开发语言·javascript