关于使用TypeScript、React的useRef Hook和React的ref的一切

330 阅读2分钟

简单总结一下如何使用React的useRef Hook来使用TypeScript的ref。首先,React中的ref主要是用来分配一个HTML元素的。被分配的HTML元素给了我们必须的读写操作,这让我们可以程序化地调用函数。以下面的例子为例,聚焦一个输入元素:

import * as React from 'react';
const App = () => {  const ref = React.useRef();
  React.useEffect(() => {    if (ref.current) {      ref.current.focus();    }  }, []);
  return <input ref={ref} />;};
export default App;

当在TypeScript中使用这个带有useRef钩的函数组件时,你很可能会遇到一个错误。最好的做法是用null来初始化ref。此外,你必须使用一个类型参数,将 ref 转换成 HTMLInputElement 的类型,并将其作为ref 属性使用的元素:

import * as React from 'react';
const App = () => {  const ref = React.useRef<HTMLInputElement>(null);
  React.useEffect(() => {    if (ref.current) {      ref.current.focus();    }  }, []);
  return <input ref={ref} />;};
export default App;

如果你想为HTML元素使用一个不可变的引用,基本上已经是这样了。然而,有时你想用一个ref 作为实例变量来捕获一个值。例如,一个引用可以记录所有的点击互动:

import * as React from 'react';
const App = () => {  const [count, setCount] = React.useState<number>(0);
  const ref = React.useRef<number>(0);
  const handleIncrement = () => {    ref.current++;    setCount(count + 1);  };
  const handleDecrement = () => {    ref.current++;    setCount(count - 1);  };
  return (    <>      <button onClick={handleIncrement}>+</button>      <button onClick={handleDecrement}>-</button>
      <div>Count: {count}</div>      <div>Buttons {ref.current} times clicked</div>    </>  );};
export default App;

+-

计数。0

按钮被点击0次

类似的例子,但有一个复杂的对象,我们把类型参数提取为接口:

import * as React from 'react';
interface CounterTracker {  increment: number;  decrement: number;}
const App = () => {  const [count, setCount] = React.useState<number>(0);
  const ref = React.useRef<CounterTracker>({    increment: 0,    decrement: 0,  });
  const handleIncrement = () => {    ref.current.increment++;    setCount(count + 1);  };
  const handleDecrement = () => {    ref.current.decrement++;    setCount(count - 1);  };
  return (    <>      <button onClick={handleIncrement}>+</button>      <button onClick={handleDecrement}>-</button>      <div>Count: {count}</div>
      <div>        Buttons {ref.current.increment + ref.current.decrement}{' '}        times clicked      </div>
      <div>Increment clicked: {ref.current.increment}</div>      <div>Decrement clicked: {ref.current.decrement}</div>    </>  );};
export default App;

+-

计数。0

按钮0次被点击

递增点击:0

递减点击次数:0

如果你碰巧在React的useRef Hook中以一个没有初始化的实例变量开始,但在代码的后面,那么你将不得不以null初始化React的useRef Hook,并使用基于实际类型的联合类型和null作为类型参数。

import * as React from 'react';
const App = () => {  const [seconds, setSeconds] = React.useState<number>(0);  const [toggle, setToggle] = React.useState<boolean>(false);
  const ref = React.useRef<NodeJS.Timeout | null>(null);
  const toggleStopwatch = () => {    setToggle(!toggle);  };
  const resetStopwatch = () => {    setToggle(false);    setSeconds(0);  };
  React.useEffect(() => {    ref.current = setInterval(() => {      if (toggle) setSeconds((state) => state + 1);    }, 1000);
    return () => {      if (ref.current) clearInterval(ref.current);    };  }, [toggle]);
  return (    <>      <div>{seconds}</div>
      <button onClick={toggleStopwatch}>        {toggle ? 'Stop' : 'Start'}      </button>
      <button onClick={resetStopwatch}>Reset</button>    </>  );};
export default App;

0

StartReset

基本上这就是你需要知道的关于使用TypeScript、React的useRef Hook和React的ref的一切。毕竟,通过利用元素上的ref属性,ref被用作HTML元素,或者作为实例变量来跟踪一个不会导致React重新渲染的状态。如果你碰巧发现用TypeScript使用React ref的其他变化,请告诉我,我会把它们添加到本指南中。