DEV Community

Mitchell
Mitchell

Posted on

Timer - Custom Hooks

You can find all the code in this post on the repo Github.


Timer-related React custom hooks challenges


useCountdown()

import { useEffect, useRef, useState } from "react";

function useCountdown(initialValue) {
  const [count, setCount] = useState(initialValue);
  const [isActive, setIsActive] = useState(false);
  const intervalRef = useRef(null);

  function start() {
    setIsActive(true);
  }

  function pause() {
    setIsActive(false);
    clearInterval(intervalRef.current);
  }

  function reset() {
    setCount(initialValue);
    setIsActive(false);
    clearInterval(intervalRef.current);
  }

  useEffect(() => {
    if (isActive && count > 0) {
      intervalRef.current = setInterval(() => {
        setCount((prevCount) => prevCount - 1);
      }, 1000);
    } else if (count === 0) {
      clearInterval(intervalRef.current);
    }

    return () => clearInterval(intervalRef.current);
  }, [count, isActive]);

  return { count, isActive, start, pause, reset };
}

export default function App() {
  const { count, isActive, start, pause, reset } = useCountdown(10);

  return (
    <div>
      <h1>Countdown: {count}</h1>
      <button onClick={start} disabled={isActive}>
        Start
      </button>
      <button onClick={pause} disabled={!isActive}>
        Pause
      </button>
      <button onClick={reset}>Reset</button>
      {count === 0 && <p>Time's up!</p>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useDebounce()

import { useState, useEffect } from "react";

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timerId = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(timerId);
  }, [value, delay]);

  return debouncedValue;
}

/* Usage example */

export default function App() {
  const value = useDebounce(2, 2000);

  return (
    <div>
      <p>Value: {value}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useInterval()

import { useEffect, useRef, useState } from "react";

function useInterval(callbackFn, delay) {
  const callbackFnRef = useRef(callbackFn);

  useEffect(() => {
    callbackFnRef.current = callbackFn;
  }, [callbackFn]);

  useEffect(() => {
    const timerId = setInterval(() => {
      callbackFnRef.current();
    }, delay);

    return () => clearInterval(timerId);
  }, [delay]);
}

/* Usage example */

export default function App() {
  const [count, setCount] = useState(0);

  useInterval(() => {
    setCount((prev) => prev + 1);
  }, 1000);

  return <div>Count: {count}</div>;
}
Enter fullscreen mode Exit fullscreen mode

useThrottle()

import { useRef, useCallback, useState } from "react";

function useThrottle(callback, delay) {
  const lastCall = useRef(0);
  const timerId = useRef(null);

  const throttledFunction = useCallback((...args) => {
    const now = Date.now();

    if (lastCall.current === 0 || now - lastCall.current >= delay) {
      lastCall.current = now;
      callback(...args);
    } else if (!timerId.current) {
      timerId.current = setTimeout(() => {
        lastCall.current = Date.now();
        callback(...args);
        timerId.current = null;
      }, delay - (now - lastCall.current));
    }
  }, [callback, delay]);

  return throttledFunction;
}

export default function App() {
  const [count, setCount] = useState(0);

  const handleScroll = () => {
    setCount((prevCount) => prevCount + 1);
    console.log("Scroll event triggered", count);
  };

  const throttledScroll = useThrottle(handleScroll, 1000); // Throttle to 1 second

  return (
    <div
      onScroll={throttledScroll}
      style={{ height: "200vh", padding: "20px" }}
    >
      <h2>Scroll down to see throttling in action!</h2>
      <p>Scroll Count: {count}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

useTimeout()

import { useEffect, useRef, useState } from 'react';

function useTimeout(callbackFn, delay) {
  const callbackFnRef = useRef(callbackFn);

  useEffect(() => {
    callbackFnRef.current = callbackFn;
  }, [callbackFn]);

  useEffect(() => {
    const timerId = setTimeout(() => {
      callbackFnRef.current();
    }, delay);

    return () => clearTimeout(timerId);
  }, [delay]);
}

/* Usage example */

export default function App() {
  const [count, setCount] = useState(0);

  useTimeout(() => {
    setCount((prev) => prev + 1);
  }, 1000);

  return <div>Count: {count}</div>;
}
Enter fullscreen mode Exit fullscreen mode

Reference

Top comments (0)