What am I doing wrong for this Countdown timer implementation

module Timer = {
  @react.component
  let make = (~duration: timerProps) => {
    // let (minutes, setMinutes) = React.useState(_ => duration)
    // let (seconds, setSeconds) = React.useState(_ => 0)
    let ((minutes, seconds), setCountdown) = React.useState(_ => (
      duration.initMins,
      duration.initSecs,
    ))
    let interval = ref(Js.Nullable.null)

    React.useEffect(() => {
      let tick = () => {
        Js.log3("##", minutes, seconds)
        if minutes === 0 && seconds === 0 {
          Js.Nullable.iter(interval.contents, (. intervalId) => Js.Global.clearInterval(intervalId))
        } else if seconds == 0 {
          setCountdown(_ => (minutes - 1, 60))
        } else {
          setCountdown(_ => (minutes, seconds - 1))
        }
      }
      interval := Js.Nullable.return(Js.Global.setInterval(intervalid => tick(intervalid), 1000))

      // Js.Nullable.iter(interval.contents, (. intervalId) => Js.Global.clearInterval(intervalId))
      None
    })

    <div>
      {minutes === 0 && seconds === 0
        ? React.null
        : <h1>
            {React.string(`${Belt.Int.toString(minutes)} : `)}
            {seconds < 10
              ? React.string(`0${Belt.Int.toString(seconds)}`)
              : React.string(Belt.Int.toString(seconds))}
          </h1>}
    </div>
  }
}

My countdown is not working as expected. This is counting minutes back from 60 every second

I think the main problem is the useEffect hook. It will be triggered on each render as it doesn’t have any dependencies or an empty array as second parameter.
Probably you should use useEffect0 instead.

Unfortunately you will run in the next problem. If the interval will be created only once, minutes and hours in the callback will always have the initial value. So you could use

setCountdown(((minutes, seconds)) =>  {
  // these values are the updated ones
})

to use the correct value of your countdown state.

Or you could store the countdown value in a useState hook (like you’re doing it right now) to update the ui and an additional useRef hook to use the updated value in your timeout. (search for useStateRef or useRefState or something for inspiration (e.g. react-usestateref - npm)).