一、useRef
1、定义
当你想要记住某些信息,但又不想触发组件的重新渲染时,就可以使用ref;
ref是一个普通的JS对象,具有可以被读取和修改的current属性;
该ref对象在React的整个生命周期中都存在;ref存储的是不影响组件视图的信息;
ref与state的区别就是:state更改会触发组件的重新渲染,但是更改ref不会;
2、语法与参数
const ref = useRef(initialValue); // useRef用于创建一个JS对象,每次渲染返回的都是同一个ref对象;
(1)initialValue
useRef调用时赋值给ref对象的current属性的值,该值可以是任意类型,只在首次渲染时使用;
(2)返回值
useRef函数调用返回一个只有一个current属性的对象;在后续过程中,useRef返回的都是同一个对象;
修改ref的current属性时,React不会重新渲染组件;【ref是一个普通的JS对象,改变ref不会触发组件的重新渲染】
3、useRef的使用场景
- 操作DOM节点;
- 缓存存储信息,如定时器ID等;
4、自定义组件ref的获取
使用forwardRef包裹子组件;
import { forwardRef } from 'react';
const MyInput = forwardRef(({ value, onChange }, ref) => {
return (
<input value={value} onChange={onChange} ref={ref} />
);
});
export default MyInput;
5、【实践】useRef在原生DOM节点上的使用
(1)示例
// 类型声明:正确
const imgRef = useRef<HTMLImageElement>(null);
// 类型声明:错误(原因:类型不匹配【声明类型为HTMLImageElement | null,初始赋值为undefined】)
/**
* 报错方式:
* Type 'HTMLImageElement | null | undefined' is not assignable to type 'HTMLImageElement | null'.
* Type 'undefined' is not assignable to type 'HTMLImageElement | null'.
*/
const imgRef = useRef<HTMLImageElement | null>();
<img ref={imgRef}/>
(2)在Input标签上的使用
// 与ts结合使用,注意声明ref类型:input
const inputRef = useRef<Input>(null);
// input标签的使用
<input ref={inputRef} placeholder="请输入内容"/>
// input标签获取焦点
useEffect(() => {
inputRef.current?.focus();
// 赋值 inputRef.current?.setValue('赋值');
}, []);
(3)自定义ref类型
// 自定义useRef类型:
const ExampleRef = forwardRef<ref类型, Props>((_, ref) => {
React.useImperativeHandle(ref, () => ({
name // 暴露给父组件的参数
});
});
// 在父组件中的使用
const examRef = useRef<ref类型(自定义类型))>(null);
<ExampleRef ref={examRef}/>
6、为什么依赖数组中可以省略ref?
因为ref具有稳定的标识:React保证每次渲染中调用useRef产生的对象的引用都是相同的,它不会导致组件的重新渲染;
但,如果ref是从组件传递的,则必须在依赖数组中指定它;
二、useRef源码
1、mountRef - 首次渲染
function mountRef<T>(initialValue: T): {current: T} {
const hook = mountWorkInProgressHook();
const ref = {current: initialValue};
hook.memoizedState = ref; // 将值缓存在memoizedState中
return ref;
}
可以看到,在首次渲染调用useRef时,React会创建一个具有current属性的对象ref,且current的值将会被initialValue赋值,然后会将这个对象ref存储在memoizedState中;
2、updateRef - 更新渲染
function updateRef<T>(initialValue: T): {current: T} {
const hook = updateWorkInProgressHook();
return hook.memoizedState; // 直接返回我们之前缓存的对象
}
当更新时,React会直接返回memoizedState对象,这与首次渲染时的对象是同一个引用;这也就是为什么更改ref不会触发重新渲染。