Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type for useRef when used with setInterval/clearInterval [react-typescript]

I am setting up a useRef object to hold a NodeJS.Timeout component, the return type of setInterval(). When I use that later in clearInterval, I get an error (shown below) on both instances of intervalRef.current in the code.

What is the correct way to set this up so I can call clearInterval in the button onClick event?

function HookTimer(): ReactElement {

  const [timer, setTimer] = useState(0)

  let intervalRef = useRef<NodeJS.Timeout>()

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      setTimer(prevTimer => prevTimer + 1)
    }, 1000)
    return () => {
      clearInterval(intervalRef.current)
    }
  }, [])

  return (
    <div>
      Hook Timer - {timer}    
      <div>
        <button onClick={() => clearInterval(intervalRef.current)}>Clear Hook Timer</button>
      </div>
    </div>
  )

No overload matches this call. Overload 1 of 2, '(intervalId: Timeout): void', gave the following error. Argument of type 'Timeout | undefined' is not assignable to parameter of type 'Timeout'. Type 'undefined' is not assignable to type 'Timeout'. Overload 2 of 2, '(handle?: number | undefined): void', gave the following error. Argument of type 'Timeout | undefined' is not assignable to parameter of type 'number | undefined'. Type 'Timeout' is not assignable to type 'number'.ts(2769)

like image 288
Kerry Avatar asked Apr 02 '26 15:04

Kerry


1 Answers

const intervalRef = React.useRef<null | NodeJS.Timeout>(null);

React.useEffect(() => {
    intervalRef.current = setInterval(() => {
        console.log('runs every 2 seconds');
    }, 2000);

    return () => {
        console.log('clearInterval');
        return clearInterval(intervalRef.current as NodeJS.Timeout);
    };
}, []);

On initialization intervalRef is set to null.

setInterval() returns a NodeJS.Timeout type.

clearInterval(handle) definition says it expects the handle to be of type number | undefined (actually NodeJS.Timeout | undefined). As intervalRef.current could be of type null or NodeJS.Timout (as defined on intervalRef) we need to tell it that the handle should be treated as NodeJS.Timeout.

Update

Just for the sake of completeness: A very verbose alternative solution could be a guard:

// ...

React.useEffect(() => {
    // ...

    return () => {
        console.log('clearInterval');

        if (intervalRef.current !== null) {
            return clearInterval(intervalRef.current);
        }
    };
}, []);
like image 166
frehder Avatar answered Apr 04 '26 05:04

frehder