'RequestAnimationFrame so lag when update the millisecond time ONLY first time

I just want to create the tap-game. find the fastest time for tap 100 images.

The first, I fetch 50 images from my API. each image size about 3-4mb.

After that I use RequestAnimationFrame for count the time. and I do not know why in the fast time I click. millisecond timer so laggy when I click the image. But after game end and click try again button. the timer will be normally.

Is it about size of image is so big ? if it is related, I want to know why after the first time it normally.

Could someone explain me why. And How can I fix it?

I use React and here is my code

import { useState, useEffect, useRef } from "react";
import "./App.css";

const Hidden = () => {
  const [loading, setLoading] = useState(true);
  const [start, setStart] = useState(false);
  const [end, setEnd] = useState(false);
  const [state, setState] = useState({
    images: [],
    current: 0,
  });

  const [timer, setTimer] = useState(0);

  const intervalRef = useRef();

  useEffect(() => {
    const fetchData = async () => {
      const x = [...new Array(50)].map(
        async (x, i) =>
          await fetch(`http://localhost:1234/images/${i}.png`)
      );
      const result = await Promise.all(x);
      const blobs = await Promise.all(result.map(async (x) => await x.blob()));
      const objUrl = blobs.map((x) => URL.createObjectURL(x));

      return objUrl;
    };

    const constructImage = async () => {
      const newImages = await fetchData();

      setState((prev) => ({ ...prev, images: newImages }));
      setLoading(false);
    };

    constructImage();
  }, []);

  useEffect(() => {
    if (start) {
      const startTime = Date.now();

      function timer() {
        intervalRef.current = requestAnimationFrame(timer);

        const elapsedTime = Date.now() - startTime;
        const formatTime = (elapsedTime / 1000).toFixed(3);
        setTimer(formatTime);
      }
      intervalRef.current = requestAnimationFrame(timer);
    }

    return () => cancelAnimationFrame(intervalRef.current);
  }, [start]);

  useEffect(() => {
    if (state.images.length > 0)
      if (state.current === state.images.length) setEnd(true);
  }, [state.current]);

  const nextImage = () => {
    if (!start) setStart(true);
    if (state.current === state.images.length - 1) setStart(false);
    setState((prev) => ({ ...prev, current: prev.current + 1 }));
  };

  const reset = () => {
    setStart(false);
    setEnd(false);
    setState((prev) => ({ ...prev, current: 0 }));
    setTimer(0);
    intervalRef.current = null;
  };

  return (
    <div className="App">
      <header className="App-header">
        {loading && <div>Loading ....</div>}
        <>
          {!loading && !end && state.current + 1}
          {end && (
            <div>
              <h1>End!</h1>
              <h3>time: {timer}</h3>
              <button onClick={reset}>try again</button>
            </div>
          )}
          {!loading &&
            state.images.map((image, i) => (
              <img
                key={i}
                src={image}
                alt={i}
                style={{
                  display: i === state.current ? "block" : "none",
                  width: "20rem",
                }}
                onClick={nextImage}
              />
            ))}
          {!loading && !end && <span>{timer}</span>}
        </>
      </header>
    </div>
  );
};

export default Hidden;

and example is here https://l9wyqz.csb.app/



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source