Skip to content

useRef

什么是 useRef

useRef 用于在函数式组件中创建一个引用(ref),这个引用可以用来存储一个不随组件重新渲染而改变的值,并且这个引用的改变也不会触发组件的重新渲染。

因此useRef常见的两个用途是:访问DOM元素和跨渲染周期存储数据。

ref 和 state 的区别

refstate
useRef(value) 返回 {current: value}useState(value) 返回 [value, setValue]
更改时不触发重新渲染更改时触发重新渲染
可变——可以在渲染过程之外更新值“不可变”——必须通过设置函数更新值,从而排队重新渲染
不应在渲染期间读取或写入值可随时读取,但每次渲染都有一个不变的state快照

基本用法

useRef可以存储任意JavaScript数据类型,可以通过ref.current来获取或修改存储的值,但不会触发重新渲染。

tsx
import { useRef } from "react";

export default function UseRefBasic() {
    const ref = useRef(0);
    
    function handleClick() {
        ref.current ++;
        alert(`You clicked ${ref.current} times!`);
    }

    return (
        <div>
            <button onClick={handleClick}>click</button>
        </div>
    );
}

操作DOM元素

useRef可以用来访问DOM元素,通过ref.current可以用来访问DOM元素的属性和方法。

为什么使用useRef操作DOM元素

  1. 因为useRef可以跨渲染周期存储数据,所以可以用来存储DOM元素的引用。
  2. 因为useRef的引用不会触发组件的重新渲染,所以可以用来操作DOM元素,而不会引起性能问题。
tsx
import { useRef } from "react";

export default function UseRefDOM() {
    const ref = useRef<HTMLDialogElement>(null);

    function handleOpen() {
        if (ref.current) {
            ref.current.showModal();
        }
    }

    function handleClose() {
        if (ref.current) {
            ref.current.close();
        }
    }

    return (
        <>
            <dialog ref={ref}>
                <p>Open Dialog Success</p>
                <button onClick={handleClose}>OK</button>
            </dialog>
            <button onClick={handleOpen}>Open Dialog DOM</button>
        </>
    );
}

forwardRef

默认情况下,React 不允许访问子组件的 DOM 节点,但是可以通过forwardRef来转发ref到子组件。

tsx
import { forwardRef, useRef } from "react";

const MyDialog = forwardRef<HTMLDialogElement>((_, ref) => {

    function handleClick() {
        if (ref) {
            (ref as React.MutableRefObject<HTMLDialogElement | null>)
                .current?.close();
        }
    }

    return (
        <>
            <dialog ref={ref}>
                <p>Open Dialog Success</p>
                <button onClick={handleClick}>OK</button>
            </dialog>
        </>
    );
});

export default function UseRefForward() {
    const ref = useRef<HTMLDialogElement>(null);

    function handleClick() {
        if (ref) {
            ref.current?.showModal();
        }
    }

    return (
        <div>
            <MyDialog ref={ref} />
            <button onClick={handleClick}>Open Dialog forwardRef</button>
        </div>
    );
}