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 renderingconsole.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>;}
