HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavascriptTip

React useRequestAnimationFrame hook

Submitted by: @import:30-seconds-of-code··
0
Viewed 0 times
hookreactuserequestanimationframe

Problem

The requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser calls a specified function to update an animation before the next repaint. This method provides a more efficient way to perform animations in JavaScript. But what about using it in React?
As usual, a custom hook is the answer. Using the useRef() hook, you can create two variables to hold the last request id and the last timestamp. You can then define a function, animate, which updates these variables, runs the callback, and calls Window.requestAnimationFrame() perpetually.
Finally, you can use the useEffect() hook to initialize the value of requestRef using Window.requestAnimationFrame() and clean up when the component unmounts.

Solution

const useRequestAnimationFrame = callback => {
  const requestRef = React.useRef();
  const previousTimeRef = React.useRef();

  const animate = time => {
    if (previousTimeRef.current) callback(time - previousTimeRef.current);
    previousTimeRef.current = time;
    requestRef.current = requestAnimationFrame(animate);
  };

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

const Counter = () => {
  const [count, setCount] = React.useState(0);

  useRequestAnimationFrame(deltaTime => {
    setCount(prevCount => (prevCount + deltaTime * 0.01) % 100);
  });

  return <p>{Math.round(count)}</p>;
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <Counter />
);


Finally, you can use the useEffect() hook to initialize the value of requestRef using Window.requestAnimationFrame() and clean up when the component unmounts.

Code Snippets

const useRequestAnimationFrame = callback => {
  const requestRef = React.useRef();
  const previousTimeRef = React.useRef();

  const animate = time => {
    if (previousTimeRef.current) callback(time - previousTimeRef.current);
    previousTimeRef.current = time;
    requestRef.current = requestAnimationFrame(animate);
  };

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

const Counter = () => {
  const [count, setCount] = React.useState(0);

  useRequestAnimationFrame(deltaTime => {
    setCount(prevCount => (prevCount + deltaTime * 0.01) % 100);
  });

  return <p>{Math.round(count)}</p>;
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <Counter />
);

Context

From 30-seconds-of-code: use-request-animation-frame

Revisions (0)

No revisions yet.