useRef 接受一个初始值,返回一个reference (一个有 current 属性的对象)

关于references的2条规则

  1. reference的值是持续的( persisted ),重新渲染也会保持和渲染前相同
  2. 更新 reference 不会触发组件重新渲染

    1. 可变值(Mutable values)

    使用 useRef

    1. import { useRef } from 'react';
    2. function LogButtonClicks() {
    3. const countRef = useRef(0);
    4. const handle = () => {
    5. countRef.current++;
    6. console.log(`Clicked ${countRef.current} times`);
    7. };
    8. console.log('I rendered!');
    9. return <button onClick={handle}>Click me</button>;
    10. }

    改用useState

    1. import { useState } from "react"
    2. const LogButtonClicks = () => {
    3. const [count, setCount] = useState(0)
    4. const handle = () => {
    5. setCount(count + 1)
    6. console.log(`Clicked ${count} times`) // 此时并没有更新
    7. }
    8. console.log('I rendered!')
    9. console.log(count)
    10. return <button onClick={handle}>Click me</button>
    11. )
    12. }

    useRef 和 useState 的区别

  • useRef不会触发重新渲染,useState会触发重新渲染
  • ref 的值的更新是同步的(立马更新), state的值的更新是异步的( 渲染后更新 )

应用: stopwatch

将 timerId 存储在 ref 里,使其可持续
如果一开始用 let timerId = null 来声明timerId, 则每次渲染都会被重置
我们习惯用 reference 存储基础数据 (infrastructure data)

  1. import { useEffect, useRef, useState } from "react"
  2. const Stopwatch = () => {
  3. const timerIdRef = useRef<any>(0)
  4. const [count, setCount] = useState(0)
  5. const startHandler = () => {
  6. if ( timerIdRef.current ) {return}
  7. timerIdRef.current = setInterval(() => setCount(c => c+1), 1000)
  8. }
  9. const stopHandler = () => {
  10. clearInterval(timerIdRef.current)
  11. timerIdRef.current = 0
  12. }
  13. const resetHandler = () => {
  14. setCount(0)
  15. clearInterval(timerIdRef.current)
  16. timerIdRef.current = 0
  17. }
  18. useEffect(() => {
  19. return () => {
  20. console.log('bey~')
  21. clearInterval(timerIdRef.current)
  22. }
  23. }, [])
  24. return (
  25. <div>
  26. <div>Timer: {count}s</div>
  27. <div>
  28. <button onClick={startHandler}>Start</button>
  29. <button onClick={stopHandler}>Stop</button>
  30. <button onClick={resetHandler}>Reset</button>
  31. </div>
  32. </div>
  33. )
  34. }
  35. export default Stopwatch

2. 访问DOM节点

  1. 定义一个空的 reference const elementRef = useRef();
  2. DOM元素的 ref 属性和定义的reference绑定:
    ;
  3. 在元素挂载之后,reference就指向了DOM元素

    1. import { useRef, useEffect } from 'react';
    2. function InputFocus() {
    3. const inputRef = useRef();
    4. useEffect(() => {
    5. // Logs `HTMLInputElement`
    6. console.log(inputRef.current);
    7. inputRef.current.focus();
    8. }, []);
    9. // Logs `undefined` during initial rendering
    10. console.log(inputRef.current);
    11. return <input ref={inputRef} type="text" />;
    12. }

    3. 注意事项

    reference的更新应该在 useEffect() 的回调函数里或者在事件处理函数里 (event handlers, timer handlers, etc).

    1. import { useRef, useEffect } from 'react';
    2. function MyComponent({ prop }) {
    3. const myRef = useRef(0);
    4. useEffect(() => {
    5. myRef.current++; // Good!
    6. setTimeout(() => {
    7. myRef.current++; // Good!
    8. }, 1000);
    9. }, []);
    10. const handler = () => {
    11. myRef.current++; // Good!
    12. };
    13. myRef.current++; // Bad!
    14. if (prop) {
    15. myRef.current++; // Bad!
    16. }
    17. return <button onClick={handler}>My button</button>;
    18. }

原文地址