0

I have a dice component, and i want it to spit random values n times, and after that show a static value, and this should happen each time props is updated. so i used setInterval in the following code:

  //iter - keep count on how many times we rendered a value
  const [iter, setIter] = useState(0);

  //keep the intervalId given throughout the renderes
  const [intervalId, setIntervalId] = useState(undefined);

  //check each render(iter update) if iter has reached 0, to clear the interval
  useEffect(() => {
    if (iter <= 0) {
      clearInterval(intervalId);
    }
  }, [iter]);

  //if the props updated, call roll dice
  useEffect(() => {
    rollDice();
  }, [props]);

  const rollDice = () => {
    const interval = setInterval(() => {
      //reduce iter every 100ms
      setIter((prev) => prev - 1);
    }, 100);

    //run this interval 10 times
    setIter(10);
    setIntervalId(interval);
  };

This is what the component returns:

 {props.values.map((val, i) => (
    <FontAwesomeIcon
      key={i}
      //randomize icons
      icon={dice[iter ? Math.floor(Math.random() * 5) : val - 1]}
      size={props.size || "4x"}
      color={props.color || "white"}
    />
  ))}

But for some reason i get an infinite loop, the first useEffect keeps firing. Why does this happen and how can i avoid this kind of a bug in the future?

Thank you.

1 Answer 1

1

I think the problem is because you are using state for storing local variables. You call setIter to update iter, but setIter is asynchronous. So it won't update immediately which means that iter might skip 0 and go into negatives numbers which will be infinite if you just check if iter is different than 0. But of course it will work (sort of) if you check that iter is greater than 0.

You should replace your iter state with a ref:

const iter = useRef(0);

Then you can update iter by using its current value:

iter.current = 10;
iter.current--;

Then your icon code will be:

icon={dice[iter.current ? Math.floor(Math.random() * 5) : val - 1]}

Likewise, intervalId should not be stored in state but in a ref:

const intervalId = useRef();
Sign up to request clarification or add additional context in comments.

3 Comments

I'm now storing the itevalId in a ref, but i cant store iter in a ref, because i need the component to render each time iter updates. And i have only now noticed that the infinite loop continues, but the render output stays the same. But thank you for the ref suggestion
That's the thing, the component will not render each time state iter updates, because iter won't update every time you call setIter. Not only that but there is no guarantee that your interval function will be called 10 times. If you really want 10 times, the only way is by using a ref. But you can have another state that you update at the same time, and this one will be used for rendering
Thank you, it worked, this is what i did: pastebin.com/MEjhUuwT , If this is not what you meant then pleas clarify what i did wrong, But anyway It all makes sense now, Thank you :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.