Skip to content

Instantly share code, notes, and snippets.

@mfrancois3k
Forked from tsmx/react-counter-func.md
Created March 21, 2022 16:56
Show Gist options
  • Save mfrancois3k/649b01b331a208c9c47bf1c222b55c4f to your computer and use it in GitHub Desktop.
Save mfrancois3k/649b01b331a208c9c47bf1c222b55c4f 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