June 11, 2020 • 2 min read
React hooks are useful as they allow you to encapsulate and reuse stateful logic in your components.
setTimeout
is a useful JavaScript method that creates a timer and executes a function or piece of code when
the timer expires.
In this example, we will create a hook called useSetTimeout
that encapsulates the code for setTimeout
, while providing
some extra functionalities mentioned below.
Displaying or updating the UI after a certain period of time.
Provide user feedback in stages by using multiple useSetTimeout calls.
We will start by defining a basic version of this hook that utilizes the useSetTimeout
method.
This hook will have two parameters.
1import { useEffect } from 'react'23// By default, callback will be an empty function in case the4// user does not need to run a specific function.5export function useSetTimeout(delay, callback = () => {}) {6useEffect(() => {7// returns a positive interger ID which identifies the timer8const timerId = setTimeout(callback, delay)9// clear the timer10return () => clearTimeout(timerId)11}, [delay, callback])12}
A few things are being done here.
callback
and delay
are provided to the useEffect's dependency array so that the effect is ran anytime those
values change.
We will call the useState
hook to create state for the timer.
Our useState call will return a value we call isRunning
that is true by default, and a function to update isRunning, called
setIsRunning
. Instead of passing callback directly to setTimeout, we now create a new function that will run the callback
and call setIsRunning
with a value of false, updating isRunning
.
Finally, we return isRunning so the caller can use it to determine if the timer has expired.
1import { useEffect, useState } from 'react'23// By default, callback will be an empty function in case the4// user does not need to run a specific function.5export function useSetTimeout(delay, callback = () => {}) {6const [isRunning, setIsRunning] = useState(true)78useEffect(() => {9// returns a positive interger ID which identifies the timer10const timerId = setTimeout(() => {11callback()12setIsRunning(false)13}, delay)14// clear the timer15return () => clearTimeout(timerId)16}, [delay, callback])1718return isRunning19}
How about if we want the caller to be able to reset the timer?
In this case, we return the setter function, setIsRunning
, so the caller can have access to it.
Finally, we need to check if the timer is in a state of running, and if it is, we call setTimeout.
Here is what the code will look like:
1import { useEffect, useState } from 'react'23// By default, callback will be an empty function in case the4// user does not need to run a specific function.5export function useSetTimeout(delay, callback = () => {}) {6const [isRunning, setIsRunning] = useState(true)78useEffect(() => {9let timerId1011if (isRunning) {12// returns a positive interger ID which identifies the timer13timerId = setTimeout(() => {14callback()15setIsRunning(false)16}, delay)17}1819// clear the timer20return () => clearTimeout(timerId)21}, [delay, callback, isRunning])2223return [isRunning, setIsRunning]24}
We also add isRunning
to the useEffect dependency array so the effect re-runs if the value of isRunning
changes.
Here is an example of using this hook:
1...23function App() {4const [isRunning, setIsRunning] = useSetTimeout(2000)56return (7<div>8{isRunning ? "Loading..." : "Done"}9<button onClick={() => setIsRunning(true)}>Click Me</button>10</div>11)12}1314...
After 2 seconds, isRunning
will be set to false, but whenever the user clicks on the button, the timer will reset and
wait another 2 seconds before setting it back to false.
The last requirement of this hook is to control when the timer starts. Right now, the timer starts as soon as the hook is called. Update the hook so that a timer can be started in a controlled fashion.
How can you use this hook to display different messages to a user after 3, 5 and 8 seconds of them waiting for something to load?
If you enoyed this tutorial, please consider subscribing to my newsletter for more like it.