Skip to content

React Hooks 系列 之 useRef

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传递的参数(initialValue)。返回的对象将在组件的整个生命周期内保持不变。

返回一个持久的对象

demo 代码
jsx
import { useRef, useState, useEffect } from "react";
import { Card, Button, Space } from "antd";

function Timer() {
  const [isRunning, setIsRunning] = useState(false);
  const count = useRef(0);
  const intervalRef = useRef(null);

  useEffect(() => {
    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    };
  }, []);

  const startTimer = () => {
    if (!isRunning) {
      setIsRunning(true);
      intervalRef.current = setInterval(() => {
        count.current += 1;
        console.log(`Timer has run ${count.current} times.`);
      }, 1000);
    }
  };

  const stopTimer = () => {
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
      setIsRunning(false);
    }
  };

  return (
    <Card title="案例 demo">
      <p>Check the console to see the timer count:{count.current}</p>
      <Space>
        <Button onClick={startTimer} type="primary" disabled={isRunning}>
          开始
        </Button>
        <Button onClick={stopTimer} disabled={!isRunning}>
          停止
        </Button>
      </Space>
    </Card>
  );
}

export default Timer;
import { useRef, useState, useEffect } from "react";
import { Card, Button, Space } from "antd";

function Timer() {
  const [isRunning, setIsRunning] = useState(false);
  const count = useRef(0);
  const intervalRef = useRef(null);

  useEffect(() => {
    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    };
  }, []);

  const startTimer = () => {
    if (!isRunning) {
      setIsRunning(true);
      intervalRef.current = setInterval(() => {
        count.current += 1;
        console.log(`Timer has run ${count.current} times.`);
      }, 1000);
    }
  };

  const stopTimer = () => {
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
      setIsRunning(false);
    }
  };

  return (
    <Card title="案例 demo">
      <p>Check the console to see the timer count:{count.current}</p>
      <Space>
        <Button onClick={startTimer} type="primary" disabled={isRunning}>
          开始
        </Button>
        <Button onClick={stopTimer} disabled={!isRunning}>
          停止
        </Button>
      </Space>
    </Card>
  );
}

export default Timer;

上述的 demo 展示了 useRef 的以下特性:

  1. 持久性useRef 返回的 ref 对象在组件的整个生命周期内都是持久的。

  2. 不会引起组件重新渲染:与 useState 不同,修改 useRef.current 属性不会引起组件重新渲染。在 demo 中,即使我们增加了 count.current 的值,组件也没有重新渲染。

与 DOM 交互

demo 代码
jsx
import { useRef } from "react";
import { Card, Input, Button, Space } from "antd";

function TextInputWithFocus() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <Card title="案例 demo">
      <Space>
        <Input ref={inputEl} placeholder="click button" />
        <Button onClick={onButtonClick} type="primary">
          Focus the input
        </Button>
      </Space>
    </Card>
  );
}

export default TextInputWithFocus;
import { useRef } from "react";
import { Card, Input, Button, Space } from "antd";

function TextInputWithFocus() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <Card title="案例 demo">
      <Space>
        <Input ref={inputEl} placeholder="click button" />
        <Button onClick={onButtonClick} type="primary">
          Focus the input
        </Button>
      </Space>
    </Card>
  );
}

export default TextInputWithFocus;

这个 demo 主要展示了 useRef 如何在 React 中用于直接与 DOM 元素交互。

保存上一次的值

demo 代码
jsx
import { useRef, useState, useEffect } from "react";
import { Card, Button } from "antd";

function PreviousValueComponent() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();

  useEffect(() => {
    prevCountRef.current = count;
  }, [count]);

  return (
    <Card title="案例 demo">
      <p>Current count: {count}</p>
      <p>Previous count: {prevCountRef.current}</p>
      <Button onClick={() => setCount(count + 1)} type="primary">
        Increment
      </Button>
    </Card>
  );
}

export default PreviousValueComponent;
import { useRef, useState, useEffect } from "react";
import { Card, Button } from "antd";

function PreviousValueComponent() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();

  useEffect(() => {
    prevCountRef.current = count;
  }, [count]);

  return (
    <Card title="案例 demo">
      <p>Current count: {count}</p>
      <p>Previous count: {prevCountRef.current}</p>
      <Button onClick={() => setCount(count + 1)} type="primary">
        Increment
      </Button>
    </Card>
  );
}

export default PreviousValueComponent;

这个 demo 主要展示了 useRef 如何在 React 中用于跟踪上一次的值。