<div id="root"></div>
body {
  text-align: center;
  padding: 2rem;
  font-size: 20px;
}

h1 {
  margin: 0 0 1rem;
}

button {
  font-size: 1.2rem;
  padding: 0.5rem 1rem;
}
const { useState, useRef, useEffect } = React;

const App = () => {
  const [progress, updateProgress] = useState(10);
  const change = () =>
    updateProgress((value) => {
      return value >= 100 ? 0 : value + 10;
    });

  return (
    <>
      <h1>React Percent</h1>
      <p>
        <progress value={progress} max="100">
          {progress}%
        </progress>
      </p>
      <p>
        <Progress value={progress} />
      </p>
      <p>
        <button onClick={change}>Add 10%</button>
      </p>
    </>
  );
};

const Progress = ({ value = 0, symbol = "%" }) => {
  // initialization of ref with value only happens first time
  const oldValue = useRef(value);
  const interval = useRef(null);
  const [display, setDisplay] = useState(oldValue.current);

  useEffect(() => {
    interval.current && clearInterval(interval.current);
    interval.current = setInterval(() => {
      setDisplay((val) => {
        console.log(val);
        if (val >= value) {
          oldValue.current = value;
          clearInterval(interval.current);
          return val;
        }
        return val + 1;
      });
    }, 50);

    return () => clearInterval(interval.current);
  }, [value]);

  return (
    <span>
      {display}
      {symbol}
    </span>
  );
};

ReactDOM.render(<App />, document.querySelector("#root"));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/react@16.8.6/umd/react.development.js
  2. https://unpkg.com/react-dom@16.8.6/umd/react-dom.development.js