#600 useTimeout

medium
react
hooks
timers

Implement a custom React hook useTimeout(callback, delay) that runs callback once, delay milliseconds after the component mounts.

Calling setTimeout inside useEffect looks simple, but it gets tricky once the component re-renders. Your hook should behave like this:

  • callback is called exactly once, after delay ms.
  • Re-rendering the component with the same delay must not restart the timer.
  • If a new callback is passed while the timer is running, the latest callback is the one that runs when the timer fires, and the timer is not restarted. This means an inline arrow function always sees fresh state.
  • If delay changes, the old timer is cancelled and a new one starts with the new delay.
  • If delay is null, no timer runs. Switching from null to a number starts the timer.
  • The timer is cleared when the component unmounts, so callback never runs after that.

Example

function Toast({ message, onClose }) {
  const [hovered, setHovered] = useState(false);

  // close after 3 seconds, but pause while the user hovers the toast
  useTimeout(onClose, hovered ? null : 3000);

  return (
    <div onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}>
      {message}
    </div>
  );
}

The hook does not need to return anything.