本文全方面介绍 useRef,分别从什么是 useRef、为什么使用 useRef 以及 useRef 三兄弟(指 useRef 、 forwardRef 以及 useImperativeHandle )来讲解。
本文所有示例
一、什么是useRef
const refContainer = useRef(initialValue);
- 返回一个可变的 ref 对象,该对象只有个 current 属性,初始值为传入的参数( initialValue )。
- 返回的 ref 对象在组件的整个生命周期内保持不变
- 当更新 current 值时并不会 re-render ,这是与 useState 不同的地方
- 更新 useRef 是 side effect (副作用),所以一般写在 useEffect 或 event handler 里
- useRef 类似于类组件的 this
简单示例
需求: 点击 button 的时候 选中文本框
实现:
import React, {
MutableRefObject, useRef } from 'react'
const TextInputWithFocusButton: React.FC = () => {
const inputEl: MutableRefObject<any> = useRef(null)
const handleFocus = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus()
}
return (
<p>
<input ref={
inputEl} type="text" />
<button onClick={
handleFocus}>Focus the input</button>
</p>
)
}
export default TextInputWithFocusButton
小结: 通过useRef定义个inputEl变量,在input 元素上定义ref={inputEl},这样通过inputEl.current就可以获取到input Dom 元素,选中则调用下focus函数即可
见示例库里的domUseRef.tsx
二、为什么使用useRef
需求: 跨渲染取到状态值
只用useState:
实现:
import React, {
useState } from "react";
const LikeButton: React.FC = () => {
const [like, setLike] = useState(0)
function handleAlertClick() {
setTimeout(() => {
alert(`you clicked on ${
like}`)
//形成闭包,所以弹出来的是当时触发函数时的like值
}, 3000)
}
return (
<>
<button onClick={
() => setLike(like + 1)}>{
like}赞</button>
<button onClick={
handleAlertClick}>Alert</button>
</>
)
}
export default LikeButton
现象:
在like为6的时候, 点击 alert , 再继续增加like到10, 弹出的值为 6, 而非 10.
为什么不是界面上like的实时状态?
当我们更改状态的时候,React会重新渲染组件,每次的渲染都会拿到独立的like值,并重新定义个handleAlertClick函数,每个handleAlertClick函数体里的like值也是它自己的,所以当like为6时,点击alert,触发了handleAlertClick,此时的like是6,哪怕后面继续更改like到10,但alert时的like已经定下来了。
小结:
不同渲染之间无法共享state状态值
见示例库里的likeButton
采用全局变量
在组件前定义一个类似 global 的变量
实现:
import React from "react";
let like = 0;
const LikeButton: React.FC = () => {
function handleAlertClick() {
setTimeout(() => {
alert(`you clicked on ${
like}`);