October 12, 2020

React Custom Hook: useTimer

A simple hook that waits for a boolean to be true to begin counting. The count is controlled by a function that can remain in the function component. This hook will continue to pass true to the function until the starting number time is equal to the final number limit. It is recommended to remain outside and imported to a function component.

import { useEffect } from "react";

/**
 * Timer ~ Count up or down
 * @param {boolean} bool if true, start the timer
 * @param {function} fn to control the count outside of useEffect (ex: + 2,  - 1, etc.)
 * @param {number} time a staring number that will count down or up
 * @param {number} limit the last number to reach
 */
const useTimer = (bool, fn, time = 0, limit = 0) => {
  useEffect(() => {
    const timer = setTimeout(() => fn(true), 1000);
    if (bool) {
      if (time === limit) {
        fn(false);
        clearTimeout(timer);
      }
    } else {
      return clearTimeout(timer);
    }
  }, [bool, fn, time, limit]);
};

export default useTimer;

How to Use This Hook

I have a working example for counting up or down. Here's how we can count down.

import React, { useState } from "react";
import useTimer from "../hooks/useTimer";

const CountDown = () => {
  const [start, setStart] = useState(false);
  const [time, setTime] = useState(10);

  const handleTime = (bool) => {
    if (bool) {
      setTime(() => time - 1);
    } else {
      setTime(() => 10);
      setStart(false);
    }
  };

  useTimer(start, handleTime, time); // limit is 0 by default

  return (
    <div>
      <h2>{time}</h2>
      <button onClick={() => setStart(!start)}>Count Down</button>
    </div>
  );
};

export default CountDown;

We're expecting this to count down from 10 to 0 and stop.

const [start, setStart] = useState(false);
const [time, setTime] = useState(10);

Next is the function useTimer will call. If start is true, decrease the time by 1, when it finally reaches zero - reset the boolean and time to their default values.

const handleTime = (bool) => {
    if (bool) {
      setTime(() => time - 1);
    } else {
      setTime(() => 10);
      setStart(false);
    }
  };

What's nice is that even if we didn't set anything, by default our hook will clear the timeout safely! You can also trigger different timers and it seems like it's working fairly well.

Copyright © 2021. Jake Wantulok