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

Time:10-19

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.

CodePudding user response:

You're never stopping your interval. And to only trigger the useEffect() for hasToRefresh, I would move the creation of image string outside of it.

 const VariableImage = () => {
  const imageUrl = "https://picsum.photos/200";
  const imageRefresh = "?forcerefresh=";
  const [imageNumber, setImageNumber] = useState(Math.random());
  const image = imageUrl   imageRefresh   imageNumber;
  const [hasToRefresh, setHasToRefresh] = useState(true);

  const intervalRef = useRef();

  useEffect(() => {
    if (hasToRefresh) {
      intervalRef.current = setInterval(() => {
        setImageNumber(Math.random());
      }, 1000);
    }
    return () => {
      intervalRef.current && clearInterval(intervalRef.current);
      intervalRef.current = null;
    }
  }, [hasToRefresh]);

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

Here's the updated codesandbox: https://codesandbox.io/s/variable-image-forked-oxfgc9?file=/src/VariableImage/VariableImage.js:54-896

CodePudding user response:

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;
  • Related