useRef
useRef 是一个 React Hook,它能帮助引用一个不需要渲染的值。
使用方式
const ref = useRef(initialValue)
传入的参数是一个初始值initialValue,也是ref的current属性的初始值。这个值可以是任意类型的值。
返回值是一个只有一个属性current的对象,current的初始值就是传入的initialValue。
注意事项:
ref.current
属性值可以更改,并且修改ref.current
属性值不会触发渲染。- 除了初始化渲染外,不要在渲染期间读取和写入(修改)
ref.current
的值。可以在 事件处理程序或者 Effect 中读取和写入ref.current
。 - 也就是说,
ref.current
可以用来存储一些不影响组件渲染和视图的值,以及一些需要长久留存在组件中的值。
在多次渲染中保持对一个变量的相同引用
React的每一次渲染都像是生成一张快照,也就是说每次渲染函数都拥有自己内部独立的props和state。当触发setState函数的时候,就会触发一次渲染,生成一张新的快照,渲染函数内部就会出现新的props和state。这个时候。哪怕是在渲染函数内部定义的变量,也会用新的引用,也就是Object.is
会判断为false。
import { useState } from "react"
const UseStateDemo = () => {
const [count, setCount] = useState(0)
const [msg, setMsg] = useState('msg')
const handleClick = () => {
console.log('count', count) // 0
setCount(count + 1); // 请求使用 1 重新渲染
console.log(count); // 仍然是 0!
let timer = setTimeout(() => {
clearTimeout(timer)
console.log('setTimeout', count); // 还是 0!
}, 5000);
}
return (
<div>
<div>{msg}-count: {count}</div>
<div>
<button onClick={handleClick}>add</button>
</div>
</div>
)
}
export default UseStateDemo
这也是因为倒计时函数会打印出 0 的原因。当我们第一次调用函数UseStateDemo的时候,count的值为 0 。当触发setCount的时候,react就会拿着新的count值重新渲染,也就是重新调用函数UseStateDemo。而每次渲染中的count的值是独立的,同时并不会影响正在执行的事件处理函数。所以在第一次渲染中,执行的倒计时函数中count的值仍然为 0 。
如果我们想要在倒计时函数中就能拿到最新的 count 值,或者说拿到变化后的 count 值。就可以借助 useRef
。
import { useState, useRef } from "react"
export default function UseRefDemo () {
const [count, setCount] = useState(0)
const countRef = useRef(1)
const handleClick = () => {
setCount(count + 1)
countRef.current = countRef.current + 1
console.log('count', count) // 仍然是 0!
console.log('countRef.current', countRef.current) // 变为 1
}
return (
<div>
<div>count: {count}</div>
{/* <div>countRef: {countRef.current}</div> */}
{/** useRef.current 可以参与渲染;但是不要让useRef在渲染期间读取或写入 */}
<div>
<button onClick={handleClick}>add</button>
</div>
</div>
)
}
所以useRef
还可以用来保存定时器,方便后续不需要的时候销毁定时器。
import { useState, useRef } from "react"
export default function UseRefDemo () {
const [count, setCount] = useState(0)
const countRef = useRef(1)
const setTimeRef = useRef(null)
const handleClick = () => {
setCount(count + 1)
countRef.current = countRef.current + 1
console.log('count', count) // 仍然是 0!
console.log('countRef.current', countRef.current) // 变为 1
const intervalId = setInterval(() => {
console.log('setInterval')
}, 1000)
}
const handleStopClick = () => {
const intervalId = setTimeRef.current
clearInterval(intervalId)
}
return (
<div>
<div>count: {count}</div>
{/* <div>countRef: {countRef.current}</div> */}
{/** useRef.current 可以参与渲染;但是不要让useRef在渲染期间读取或写入 */}
<div>
<button onClick={handleClick}>add</button>
<button onClick={handleStopClick}>stop</button>
</div>
</div>
)
}
通过useRef来操作DOM
在vue3中是通过ref来获取DOM元素的
<template>
<div>
<input ref="inputRef" type="text" />
</div>
<button @click="onFocus">
focus
</button>
</template>
<script setup>
import { ref } from 'vue'
const inputRef = ref(null)
const onFocus = () => {
inputRef.value.focus()
}
</script>
在react中,也可以通过 useRef
来获取DOM元素。
import { useState, useRef } from "react"
export default function UseRefDemo () {
const inputRef = useRef(null)
const onFocus = () => {
inputRef.current.focus();
}
return (
<div>
<input ref="inputRef" type="text" />
</div>
<button @click="onFocus">focus</button>
)
}