0
votes

I have a timer component which is the child component of A. I'm having play and pause as state values of the parent and passing it down to the child.

I would like to start a setTimeout for the child component when play is true, the child will get the total duration time from the hook state.

I'm using hooks to store the total Duration in the child and having a setTimeout function inside the useEffect and if the value of play is true and then overwriting the hook state value by decrementing the initial value by 1.

https://stackblitz.com/edit/react-nlxodz has 2 timers, wanted to know if what im doing in ShowTimer is a good approach or not, the cleanup function of the useEffect is called for every State update and the Timer component is not in sync with the ShowTimer even if they both start off with 10 seconds.

1
Can you provide a stackblitz example - Akhil Aravind
I made some changes and this is the updated example: stackblitz.com/edit/react-nlxodz - visizky

1 Answers

1
votes

I would say no, this is not a good approach to set a Timer, though your showTimer approach is close.

Both setTimeout() or setInterval() are not accurate and cannot be trusted. A better way would be using Date.now() approach:

const App = () => {
  const [isStart, setIsStart] = React.useState(false)
  const [duration, setDuration] = React.useState(10)
        
  const handleStart = () => setIsStart(!isStart)
  
  React.useEffect(() => {
    let interval = null
    if (!isStart || duration === 0) return
    else {
      const now = Date.now() + duration * 1000
      interval = setInterval(() => {
        const secondLeft = Math.round((now - Date.now()) / 1000)
        setDuration(secondLeft)
        if (secondLeft === 0) {
          setIsStart(false)
          clearInterval(interval)
        }
      }, 1000)
    }
    console.log("Effect render, Called because duration is changes")

    return () => {
      clearInterval(interval)
      console.log("Cleanup Effect")
    }
  }, [duration, isStart])
  
  return (
    <div className="App">
      <button onClick={handleStart} style={{ marginRight: 10 }}>
        {isStart && duration > 0 ? "Pause" : "Start"}
      </button>
      <span>{duration}</span>
    </div>
  )
}

ReactDOM.render( <App /> , document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>