12 useRef基础用法
useRef概念解释
我们第七个要学习的Hook(钩子函数)是useRef,他的作用是“勾住”某些组件挂载完成或重新渲染完成后才拥有的某些对象,并返回该对象的引用。该引用在组件整个生命周期中都固定不变,该引用并不会随着组件重新渲染而失效。
上面这段话,就算你认真读几遍,估计也是一头雾水,到底说的是啥?
我也实在想不出其他更加通俗的语言来描述useRef,不过经过下面的详细分解描述,相信能帮到你来理解useRef。
“某些组件挂载完成或重新渲染完成后才拥有的某些对象”
这句话中的“某些对象”主要分为3种:JSX组件转换后对应的真实DOM对象、在useEffect中创建的变量、子组件内自定义的函数(方法)。
第1:JSX组件转换后对应的真实DOM对象:
举例:假设在JSX中,有一个输入框<input type='text' /\>
,这个标签最终将编译转换成真正的html标签中的<input type='text'/\>
。
你应该知道以下几点:
1、JSX中小写开头的组件看似和原生html标签相似,但是并不是真的原生标签,依然是react内置组件。
2、什么时候转换? 虚拟DOM转化为真实DOM
3、什么时候可访问?组件挂载完成或重新渲染完成后
对于上面举例中的那个转换后的<input/\>
真实DOM,只有组件挂载完成或重新渲染完成后才可以访问,它就就属于“某些组件挂载完成或重新渲染完成后才拥有的某些对象”。
特别强调:useRef只适合“勾住”小写开头的类似原生标签的组件。如果是自定义的react组件(自定义的组件必须大写字母开头),那么是无法使用useRef的。
思考:如何获取这个 <input/\>
真实DOM呢?
答:用useRef。
第2:在useEffect中创建的变量:
举例,请看以下代码:
useEffect(() => {
let timer = setInterval(() => {
setCount(prevData => prevData +1);
}, 1000);
return () => {
clearInterval(timer);
}
},[]);
上述代码中,请注意这个timer是在useEffect中才定义的。
思考:useEffect 以外的地方,该如何获取这个 timer 的引用?
答:用useRef
第3:子组件内自定义的函数(方法)
由于需要结合useImperativeHandle才可以实现,而useImperativeHandle目前还未学习,所以本章中不讨论这个怎么实现。
本章只讲前2中应用场景。
“并返回该对象的引用”
上面的前2种情况,都提到用useRef来获取对象的引用。具体如何获取 ,稍后在useRef用法中会有演示。
“该引用在组件整个生命周期中都固定不变”
假设通过useRef获得了该对象的引用,那么当react组件重新渲染后,如何保证该引用不丢失?
答:react在底层帮我们做了这个工作,我们只需要相信之前的引用可以继续找到目标对象即可。
请注意:React.createRef()也有useRef相似效果,但是React.createRef无法全部适用上面提到的3种情况。
让我们回到useRef基础学习中。
useRef是来解决什么问题的?
答:useRef可以“获取某些组件挂载完成或重新渲染完成后才拥有的某些对象”的引用,且保证该引用在组件整个生命周期内固定不变,都能准确找到我们要找的对象。
具体已经在useRef中做了详细阐述,这里不再重复。
补充说明:
1、useRef是针对函数组件的,如果是类组件则使用React.createRef()。
2、React.createRef()也可以在函数组件中使用。
只不过React.createRef创建的引用不能保证每次重新渲染后引用固定不变。如果你只是使用React.createRef“勾住”JSX组件转换后对应的真实DOM对象是没问题的,但是如果想“勾住”在useEffect中创建的变量,那是做不到的。
2者都想 可以“勾住”,只能使用useRef。
注意注意
在后面useImperativeHandle的学习中,你会知道useRef还可以 “勾住并调用” 子组件内定义的函数(方法)。
以下内容更新于 2022.04.06
特别注意:修改 .current 的值并不会触发组件重新渲染
在本文开头介绍 useRef 时用了这句话 “useRef 是“勾住”某些组件挂载完成或重新渲染完成后才拥有的某些对象,并返回该对象的引用。”
也就是说 先有了 组件渲染,之后才更新了 useRef 中 .current 的值。
也就是说 useRef 变量的 current 的值实际上是 组件渲染 后的一个副产品。
这句话暗含了另外一层含义:主动更新 useRef 变量的 .current 的值并不会触发组件重新渲染。
例如下面这个示例:
import { useRef } from "react";
export default function MyButton() {
const countRef = useRef(0)
const handleClick = () => {
countRef.current = countRef.current + 1
};
return <button onClick={handleClick}>Click me {countRef.current}</button>;
}
实际运行就会发现,在点击事件中我们修改了 countRef.current 的值,尽管该值确实发生了变化,可是并不会触发组件的重新渲染。
使用 useState() 产生的变量值发生变化后,是会触发组件重新渲染的。
以上内容更新于 2022.04.06