<div id="app"></div>
const useAnimationFrame = (isRunning, callback = () => {}) => {
	const reqIdRef = React.useRef();
	const loop = React.useCallback(() => {
		if (isRunning) {
			reqIdRef.current = requestAnimationFrame(loop);
			callback();
		}
	}, [isRunning, callback]);

	React.useEffect(() => {
		reqIdRef.current = requestAnimationFrame(loop);
		return () => cancelAnimationFrame(reqIdRef.current);
	}, [loop]);
};

const Component = () => {
	const [counter, setCounter] = React.useState(0);
	const [isRunning, setIsRunning] = React.useState(false);
	const countUp = React.useCallback(() => {
		setCounter(prevCount => ++prevCount);
	});
	useAnimationFrame(isRunning, countUp);

	return (
		<div>
			<div>{counter}</div>
			<button onClick={() => setIsRunning(true)}>START</button>
			<button onClick={() => setIsRunning(false)}>STOP</button>
		</div>
	);
};

const App = () => {
	return (
		<div>
			<Component />
		</div>
	);
}

ReactDOM.render(<App />, document.getElementById('app'));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js