Search code examples
javascriptreactjsreact-nativereduxreact-hooks

How to use the latest value of the state inside a useEffect?


My component renders an image every 4 seconds. When I click on the image I want to stop rendering new images. For that I've used a useEffect hook. When I click to the image, the state hasToRefresh changes it's values, but inside useEffect it doesn't change. This is my code:

import { useEffect, useState } from "react";

const VariableImage = () => {
  const imageUrl = "https://picsum.photos/200";
  const imageRefresh = "?forcerefresh=";

  const [image, setImage] = useState(imageUrl);
  const [hasToRefresh, setHasToRefresh] = useState(true);

  useEffect(() => {
    if (hasToRefresh) {
      setInterval(() => {
        setImage(imageUrl + imageRefresh + Math.random());
      }, 4000);
    }
  }, [imageUrl, imageRefresh, hasToRefresh]);

  return (
    <>
      <img
        src={image}
        onClick={() => setHasToRefresh(!hasToRefresh)}
        alt="scenery"
        height="200"
        width="200"
      />
    </>
  );
};

export default VariableImage;

Also in sandbox: https://codesandbox.io/s/variable-image-zxhejs

How can I do for when I click the image to not render more images? If anyone could help me I would be very grateful. Thanks.


Solution

  • As Roy Schut mentioned, you never stop your timer. But the best option would be here to stop the timer when the image shouldn't be refreshed. Here's the code I would prefer.

    import { useEffect, useState, useRef } from "react";
    
    const VariableImage = () => {
      const imageUrl = "https://picsum.photos/200";
      const imageRefresh = "?forcerefresh=";
    
      const [image, setImage] = useState(imageUrl);
      const [hasToRefresh, setHasToRefresh] = useState(true);
      const intervalRef = useRef(null);
    
      useEffect(() => {
        startTimer();
    
        return () => stopTimer();
      }, []);
    
      const startTimer = () => {
        intervalRef.current = setInterval(() => {
          setImage(imageUrl + imageRefresh + Math.random());
        }, 4000);
      };
    
      const stopTimer = () => {
        clearInterval(intervalRef.current);
      };
    
      const toggleRefresh = () => {
        if (hasToRefresh) {
          stopTimer();
        } else {
          startTimer();
        }
        setHasToRefresh(state => !state);
      };
    
      return (
        <>
          <img
            src={image}
            onClick={() => toggleRefresh()}
            alt="scenery"
            height="200"
            width="200"
          />
        </>
      );
    };
    
    export default VariableImage;