1

I have project for booking. User selects days and make reservations. This reservation will be saved to mongo. Then I am fetching data from mongo and displaying it, user has 30 min for payment. If user doesn't make payment, reservation will be deleted from mongo and it will be available to another users.

here is useState to show timer

 const [timeOut, setTimeOut] = React.useState(0)

here is one line to displaying data

<th>{item.status === 'unPaid' ? `${formatTime(item.createdAt)}` : ''} </th>

then formatTime function here

const formatTime = (time) => {

       setInterval(() => {
          // 1800000 = 30 min
          const timer = new Date(time).getTime() + 1800000 
          let countDown = new Date().getTime()
          let distance = timer - countDown

          let min = Math.floor((distance % (1000*60*60) / (1000*60)))
          let sec = Math.floor((distance % (1000*60) / (1000)))
          //console.log(min, sec)  <<<<< 30 min

          let res = min + ':' + sec
          setTimeOut(res)  // <<< here is conflict 
          },1000)

     return timeOut
 }

I have problem, if user makes more than 1 reservation, I have conflict in setTimeOut(res) , It will store 2 differcent values in one spot.

Here is question, how can I fix it? How can I automaticly create new spot for timer and display it. More reservation === more useState, or any solution. If you have any idea how to fix it, please share it. Thanks

I hope it is clear!

Peret
  • 79
  • 8

1 Answers1

1

That's not how you use setInterval in React. You should change your code so that the updates you make from setInterval trigger a re-render. Basically:

  • formatTime only takes care of formatting.
  • setInterval is called from useEffect, periodically updating the state (setTimeOut) with the newly formatted time.

It should end up looking something like this:

const [timeOut, setTimeOut] = useState('')

const { createdAt } = item

useEffect(() => {
    if (item.status !== 'unPaid') return;

    const timer = window.setInterval(() => {
        setTimeOut(formatTime(createdAt));
    }, 1000);

    return () => window.clearInterval(timer);
}, [createdAt]);

return (
    ...

    <th>{item.status === 'unPaid' ? timeOut : ''} </th>

    ...
)

I would encourage you to create your own useInterval hook so that you can DRY and simplify your code by using setInterval declaratively, as Dan Abramov suggests here in Making setInterval Declarative with React Hooks.

You can find a solution to this in another answer I posted here or simply use a library I published a while back where you can find declarative version of setTimeout and setInterval, useTimeout and useInterval, and a few additional hooks written in TypeScript: https://www.npmjs.com/package/@swyg/corre.

Danziger
  • 19,628
  • 4
  • 53
  • 83