7

Im having a lot of trouble with this and i have tried various things. I want to call a function every second after i have clicked a start button and then have it paused after i click a stop button. I keep getting weird behaviour that i cant explain. How can i do this in react without classes?

somethings i have treid:

      const simulation = () => {
    if (!running) {
      console.log('hit');
      return

    } else {
      // console.log(grid);
      console.log('hey');
      setTimeout(simulation, 1000)
    }
  }

and

    enter  setInterval(() => {
    let newGrid = [...grid]
    for (let i = 0; i < numRow; i++) {
      for (let k = 0; k < numCol; k++) {
        let n = 0;
      }
    }
    console.log(grid);
  }, 5000)

I have tried a lot more, In some cases it would update the state should i have added to it but not updated it after i reset the state. How can i call a function to run every one second with updated values of state * Note the function that i want to run will update the state

Sid
  • 846
  • 3
  • 12
  • 25

2 Answers2

9

You may do the following:

  • keep track of the current counter value along with the counter on/off state in your component state;
  • employ useEffect() hook to be called upon turning counter on/off or incrementing that;
  • within useEffect() body you may call the function, incrementing count by one (if ticking is truthy, hence timer is on) with delayed execution (using setTimeout());
  • once count variable is changed in the state, useEffect() gets called once again in a loop;
  • in order to clean up the timer upon component dismantle, you should return a callback, clearing the timer from useEffect()

const { useState, useEffect } = React,
      { render } = ReactDOM,
      rootNode = document.getElementById('root')
      
const App = () => {
  const [ticking, setTicking] = useState(true),
        [count, setCount] = useState(0)
   
   useEffect(() => {
    const timer = setTimeout(() => ticking && setCount(count+1), 1e3)
    return () => clearTimeout(timer)
   }, [count, ticking])
   
   return (
    <div>
      <div>{count}</div>
      <button onClick={() => setTicking(false)}>pause</button>
      <button onClick={() => setTicking(true)}>resume</button>
    </div>
   )
}  

render (
  <App />,
  rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
Yevhen Horbunkov
  • 14,965
  • 3
  • 20
  • 42
  • Interesting, Why are you clearing the interval every time? – Sid Jul 28 '20 at 22:40
  • @ST : `clearInterval()` is, in fact, invoked [only once](https://reactjs.org/docs/hooks-effect.html#example-using-hooks-1) on component unmount – Yevhen Horbunkov Jul 28 '20 at 22:43
  • Why is the clear interval being returned? whats actually being returned and where to? – Sid Jul 28 '20 at 23:20
  • I used your answer to make my own: const [timer, setTimer] = useState(0) const incTimer = () => { setTimer(timer + 1) } useEffect(() => { console.log(running); if (!running) { return; } else { const interval = setInterval(incTimer, 1000) return () => { clearInterval(interval) } } }, [timer, running]) – Sid Jul 28 '20 at 23:28
  • The returned function in a useEffect serves as a callback when component is unmounting, so that interval get cleared at the end of the component lifecycle. – KeitelDOG Apr 05 '21 at 01:53
  • Please, add at least empty array for denpendencies yo prevent it from rendering infinitely. I almost killed my Macbook with useEffect + setInterval. The most dangerous combination I've experimented. My Macbook went off within 30 seconds, with no fan acceleration, nor processor heating. I couldn't revive it even after removing battery. After 5 hours, it started again finally. Lol – KeitelDOG Apr 09 '21 at 01:43
  • This answer would be better with explanation of the code, rather than essentially "this good?" – Heretic Monkey Jul 19 '21 at 15:07
1

Late reply but maybe interesting for some people. Look at npm package cron. In this case every 15min As easy as:

const [job] = useState(new cron.CronJob("0 */15 * * * *",async ()=>{
    await updateRiderCoords();
}));
useEffect(() => {
    job.start();
}, []);
Leo Getz
  • 69
  • 1
  • 2