DEV Community

Mitchell
Mitchell

Posted on

2

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

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Retry later