Skip to content

Instantly share code, notes, and snippets.

@tsmx
Last active March 21, 2022 16:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tsmx/cb0e6373b95ecb00f7b551ac42bf1b97 to your computer and use it in GitHub Desktop.
Save tsmx/cb0e6373b95ecb00f7b551ac42bf1b97 to your computer and use it in GitHub Desktop.
CounterFunc: functional React component for animated counting up using standard hooks

CounterFunc: functional React component for animated counting up using standard hooks

Small functional React component for counting numbers up to a certain value in a specified duration in ms. Useful for creating animated dashboards etc.

Uses React hooks useEffect, useState and useRef. Good example on how to pass props to a useEffect hook without declaring them as dependencies and how to deal with setInterval in functional components.

Usage

<CounterFunc countFrom={0} countTo={123} durationMs={400} />

Code

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

const CounterFunc = (props) => {

  const [counter, setCounter] = useState(props.countFrom);
  const propsRef = useRef(props);
  const minTimer = 50;

  useEffect(() => {
    const range = propsRef.current.countTo - propsRef.current.countFrom;
    // calc step time to show all intermediate values, never go below minTimer
    const stepTime = Math.max(Math.abs(Math.floor(propsRef.current.durationMs / range)), minTimer);
    // get current time and calculate desired end time
    const startTime = new Date().getTime();
    const endTime = startTime + propsRef.current.durationMs;
    var timer = setInterval(() => {
      const now = new Date().getTime();
      const remaining = Math.max((endTime - now) / propsRef.current.durationMs, 0);
      const value = Math.round(propsRef.current.countTo - (remaining * range));
      setCounter(value);
      if (value === propsRef.current.countTo) {
        clearInterval(timer);
        timer = null;
      }
    }, stepTime);
    // clean-up in case component unmounts before counting-up is done
    return () => {
      if (timer) {
        clearInterval(timer);
      }
    };
  }, []);

  return (
    <span>{counter}</span>
  );
}

CounterFunc.defaultProps = {
  durationMs: 400
}

export default CounterFunc;

For an equivalent class based component implementation of Counter see here.

Example

Try it out on CodeSandbox.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment