useRef 接受一个初始值,返回一个reference (一个有 current 属性的对象)
关于references的2条规则
- reference的值是持续的( persisted ),重新渲染也会保持和渲染前相同
-
1. 可变值(Mutable values)
使用 useRef
import { useRef } from 'react';
function LogButtonClicks() {
const countRef = useRef(0);
const handle = () => {
countRef.current++;
console.log(`Clicked ${countRef.current} times`);
};
console.log('I rendered!');
return <button onClick={handle}>Click me</button>;
}
改用useState
import { useState } from "react"
const LogButtonClicks = () => {
const [count, setCount] = useState(0)
const handle = () => {
setCount(count + 1)
console.log(`Clicked ${count} times`) // 此时并没有更新
}
console.log('I rendered!')
console.log(count)
return <button onClick={handle}>Click me</button>
)
}
useRef 和 useState 的区别
- useRef不会触发重新渲染,useState会触发重新渲染
- ref 的值的更新是同步的(立马更新), state的值的更新是异步的( 渲染后更新 )
应用: stopwatch
将 timerId 存储在 ref 里,使其可持续
如果一开始用 let timerId = null 来声明timerId, 则每次渲染都会被重置
我们习惯用 reference 存储基础数据 (infrastructure data)
import { useEffect, useRef, useState } from "react"
const Stopwatch = () => {
const timerIdRef = useRef<any>(0)
const [count, setCount] = useState(0)
const startHandler = () => {
if ( timerIdRef.current ) {return}
timerIdRef.current = setInterval(() => setCount(c => c+1), 1000)
}
const stopHandler = () => {
clearInterval(timerIdRef.current)
timerIdRef.current = 0
}
const resetHandler = () => {
setCount(0)
clearInterval(timerIdRef.current)
timerIdRef.current = 0
}
useEffect(() => {
return () => {
console.log('bey~')
clearInterval(timerIdRef.current)
}
}, [])
return (
<div>
<div>Timer: {count}s</div>
<div>
<button onClick={startHandler}>Start</button>
<button onClick={stopHandler}>Stop</button>
<button onClick={resetHandler}>Reset</button>
</div>
</div>
)
}
export default Stopwatch
2. 访问DOM节点
- 定义一个空的 reference const elementRef = useRef();
- DOM元素的 ref 属性和定义的reference绑定: ;
在元素挂载之后,reference就指向了DOM元素
import { useRef, useEffect } from 'react';
function InputFocus() {
const inputRef = useRef();
useEffect(() => {
// Logs `HTMLInputElement`
console.log(inputRef.current);
inputRef.current.focus();
}, []);
// Logs `undefined` during initial rendering
console.log(inputRef.current);
return <input ref={inputRef} type="text" />;
}
3. 注意事项
reference的更新应该在 useEffect() 的回调函数里或者在事件处理函数里 (event handlers, timer handlers, etc).
import { useRef, useEffect } from 'react';
function MyComponent({ prop }) {
const myRef = useRef(0);
useEffect(() => {
myRef.current++; // Good!
setTimeout(() => {
myRef.current++; // Good!
}, 1000);
}, []);
const handler = () => {
myRef.current++; // Good!
};
myRef.current++; // Bad!
if (prop) {
myRef.current++; // Bad!
}
return <button onClick={handler}>My button</button>;
}