easycodesniper

blog by chen qiyi

useRef

useRef可以用于引用一个不需要渲染的值

语法

useRef(initialValue)

  • initialValue: ref对象的current属性的初始值,只在初次渲染的时候有效

返回值

useRef返回一个只有current属性的对象。current初始值为传递的initialValue,之后可以将其设置为其他值

如果将ref对象作为一个JSX节点的ref属性,React会为它设置current属性

注意

  • ref.current属性可以修改,它是可变的,这与state不同
  • 改变ref.current属性,不会重新渲染组件
  • 除了进行初始化操作,不要在渲染期间写入或读取ref.current

不要在渲染期间写入或者读取ref.current,如果不得不在渲染期间读取或写入,应该使用state

1
2
3
4
5
6
function App() {
// ...
myRef.current = 123 // ❌ 不要再渲染期间写入 ref
// ...
return <div>{myRef.current}</div> // ❌ 不要再渲染期间读取 ref
}

事件处理程序或者Effect中读取或写入ref

1
2
3
4
5
6
7
function App() {
// ...
useEffect(() => {
myRef.current = 123
})
// ...
}

使用

useRef的使用场景主要有两种:

  • 引用或存储一些不影响视图的信息。(因为改变ref不会触发重新渲染)
  • 通过ref对象赋值给DOM的ref属性来获取或操作DOM

引用或存储一个值

示例:保存定时器的id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function App() {
let timerRef = useRef(null)

function handleStartClick() {
const timer = setTimeout(() => {
// 业务逻辑
}, 1000)
timerRef.current = timer // 保存定时器id
}

function handleStopClick() {
clearTimeout(timerRef.current)
}

return (
<div className="App">
// ....
</div>
)
}

通过ref操作DOM

1
2
3
4
5
6
7
8
9
10
11
function App() {
const inputRef = useRef(null) // 声明一个初始值为 null 的ref对象

//...

function handleClick() {
inputRef.current.focus() //inputRef.current获取到DOm节点,并调用input节点上的focus方法
}

return <input ref={ref} /> // 将DOM节点的ref对象赋值给ref
}

无法获取自定义组件的ref

默认情况下,自定义组件不会暴露他们内部DOM节点的ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { useState, useRef } from "react";

function MyComp() {
const [x, setX] = useState(0)

return <p>{x}</p>
}

function App() {

const myRef = useRef(null)

function handleClick() {
console.log(myRef.current);
}


return (
<>
<MyComp ref={myRef} />
<button onClick={handleClick}>获取ref</button>
</>
)
}

export default App;

控制台警告,无法获取到ref

为了解决这个问题,可以使用forwardRef包裹子组件,这样父组件就能得到它的ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { useEffect, useState, useRef, forwardRef } from "react";

const MyComp = forwardRef((props, ref) => { //forwardRef包裹组件,函数的第二个参数就是ref对象
const [x, setX] = useState(0)

return <p ref={ref}>{x}</p>
})

function App() {

const myRef = useRef(null)

function handleClick() {
console.log(myRef.current); //可以获取到子组件的ref
}


return (
<>
<MyComp ref={myRef} />
<button onClick={handleClick}>获取ref</button>
</>
)
}

export default App;