'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)
Solution 1:[1]
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);
}
};
}, []);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |