Today's task is to create a hook that easily uses setTimeout(callback, delay). Also:
- Reset the timer if delay changes.
- DO NOT reset the time if callback changes.
The boilerplate code:
export function useTimeout(callback: () => void, delay: number) {
// your code here
}
// if you want to try your code on the right panel
// remember to export App() component like below
// export function App() {
// return <div>your app</div>
// }
The first step is to save the latest callback in a ref. This ensures that the timeout will always call the most recent version of the function.
const savedCallback = useRef(callback)
useEffect(() => {
savedCallback.current = callback
}, [callback])
Next, the delay function. When the delay is null, the timer shouldn't run. Otherwise, it should set a new timeout when the delay changes. also, there should be a cleanup function to ensure old timeouts are cleared when the delay changes or the component unmounts.
useEffect(() => {
if(delay == null) return
const id = setTimeout(() => {
savedcallback.current()
}, delay)
return () => clearTimeout(id)
}, [delay])
To test it out, we will have 2 buttons that set the delay to 1s and 3s, respectively. The final code will look like this
import React, {useEffect, useRef, useState} from "react"
export function useTimeout(callback: () => void, delay: number) {
// your code here
const savedCallback = useRef(callback)
useEffect(() => {
savedCallback.current = callback;
}, [callback])
useEffect(() => {
if(delay == null) return;
const id = setTimeout(() => {
savedCallback.current()
}, delay);
return () => clearTimeout(id)
}, [delay])
}
// if you want to try your code on the right panel
// remember to export App() component like below
export function App() {
const[count, setCount] = useState(0);
const[delay, setDelay] = useState(3000);
useTimeout(() => {
console.log("Timeout fired:", count)
setCount((c) => c + 1);
}, delay)
return (
<div>
<p> {count} </p>
<button onClick={() => setDelay(1000)}> Set delay to 1s </button>
<button onClick={() => setDelay(3000)}> Set delay to 3s </button>
</div>
)
}
That's all folks!
Top comments (0)