-1

According to the React docs as well as every example on stackoverflow for timers, people use something similar to Option 2 w/ useEffect (+useState) to create a timer that you can start/pause/reset.

However, I've also been able to create a timer in Option 1 by solely using useState.

Why does nobody rely on useState for timers? I understand that useEffect cleans up during unmounting/re-rendering, but does this really improve performance? Wouldn't the constant unmounting and remounting from useEffect and then calling setValue be slower than just executing a regular function that calls setValue? Both options can call clearInterval, so shouldn't either be sufficient for clean-up?

Also, which timer would be more "accurate", Option 1 or 2? I believe I understand how the Event Loop for async functions works, but in React it becomes a bit foggy to me. Would there ever be a case where multiple async functions are backlogged and somehow delay useEffect from triggering and making the timer in Option 2 tick at a slower rate than Option 1 (i.e. not ticking exactly every second and slowly lagging behind the other timer)?.

Thank you!

Option 1 - regular function + useState

  const [time, setTime] = useState(1500);
  const [startPauseBtnText, setStartPauseBtnText] = useState('START');
  const timeID = useRef(null);

  const startPauseTime = () => {
    if (timeID.current) {
      clearInterval(timeID.current);
      timeID.current = null;
      setStartPauseBtnText('START');
    } else {
      timeID.current = setInterval(() => {
        setTime((prevTime) => {
          return prevTime - 1;
        });
      }, 1000);
      setStartPauseBtnText('PAUSE');
    }
  };

  const resetTime = () => {
    clearInterval(timeID.current);
    timeID.current = null;
    setTime(1500);
  }; 

Option 2 - useEffect + useState

  const [isActive, setIsActive] = useState(false);
  const [time2, setTime2] = useState(1500);

  useEffect(() => {
    let timeID2;

    if (isActive) {
      timeID2 = setInterval(() => {
        setTime2((prevTime) => {
          return prevTime - 1;
        });
      }, 1000);
    }

    return () => clearInterval(timeID2);
  }, [isActive, time2]);

  const resetTime2 = () => {
    setIsActive(false);
    setTime2(1500);
  };
Jon
  • 1
  • 1
  • I think they're both an option depending on what you need - see [this](https://stackoverflow.com/questions/53090432/react-hooks-right-way-to-clear-timeouts-and-intervals/53090848#53090848) – Nikki9696 Oct 01 '21 at 21:52
  • Separation of concerns: `useState` is by convention intended for you (and anyone else who works on React code) to contain your component's state, i.e. [the data that its render behaviour is tied to](https://reactjs.org/docs/faq-state.html). Timers are not part of that, they're independent of your component's state. They may _trigger_ a state change, but neither timers nor their values are state, and state is not a convenient data store for any and all variables you might work with. That said: the authority here are the React docs, and the React devs. Everyone else just has their own opinions. – Mike 'Pomax' Kamermans Oct 01 '21 at 21:54
  • Thanks for the quick reply Mike. However, both Options 1 and 2 use setVaue within setInterval method, with the main difference being that one is triggered by useEffect and the other is triggered by a regular function, so not completely understanding how separation of concerns is relevant here. Appreciate any clarity. – Jon Oct 01 '21 at 21:57
  • The second part is inherent to why `useEffect` exists in the first place [as per the docs](https://reactjs.org/docs/hooks-effect.html#example-using-hooks): "_**What does useEffect do?** By using this Hook, you tell React that your component needs to do something after render._" This is certainly the case for scheduling a timer: that is not part of rendering, that's a side effect you want to trigger. – Mike 'Pomax' Kamermans Oct 01 '21 at 22:03

1 Answers1

0

useEffect allows you to clear the timer when your component unmounts (via the returned cleanup function) instead of leaving it running and triggering a future state update attempt for a component that isn’t there anymore.

ray
  • 26,557
  • 5
  • 28
  • 27