在上一篇文章中,介绍了useId
的使用及其原理,这次来看一下useRef
的使用和它的原理。
useRef
的使用
https://zh-hans.react.dev/reference/react/useRef
1、 引用 DOM 元素
useRef
还可以用于引用 DOM 元素,以便在组件中直接操作这些元素。例如,获取输入框的值
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、 存储可变变量
在组件中,有时候需要存储一些变量,这些变量的变化不希望触发组件的重新渲染。例如,在一个定时器中记录当前的计数:
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
传给子组件,父组件中调用子组件方法
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 元素。
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 属性。
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 对象。
function updateRef<T>(initialValue: T): {current: T} {
// updateWorkInProgressHook 是 React 内部的一个函数,用于在当前正在处理的 Fiber 节点上获取当前的钩子实例。
const hook = updateWorkInProgressHook();
// 返回存储在钩子中的 ref 对象
return hook.memoizedState;
}
mountWorkInProgressHook
函数和updateWorkInProgressHook
函数的具体实现,可看上一篇useId的分析