useEffect does not run a named setInterval

52 views Asked by At

I have a component that accept $nextUpdateDatTime (the time with the date when the component should be updated). And useEffect doesn't want to run either setInterval or setTimeout. If I remove the name setTimeout(or setInterval), it fires, but only once, after which it does not fire. I tried to move this functionality to the parent component and transfer seconds via props - the result is exactly the same. BUT if I change something in the component code and save it (for example, add console.log), then the timer starts working! But as soon as I refresh the page, useEffect does not see and does not run setInterval(setTimeout) again.

Code:

interface IMinutesRemainingProps {
    $nextUpdateDateTime: string;
}

function MinutesRemaining({
    $nextUpdateDateTime}: IMinutesRemainingProps) {

    const [secondsToUpdate, setSecondsToUpdate] = useState(0);
    
    const changeMinutesToUpdate = () => {
        const currentDateTime = new Date().getTime();
        const nextUpdate = new Date($nextUpdateDateTime).getTime();
        
        setSecondsToUpdate(Math.round((nextUpdate - currentDateTime) / (1000)));
    }

    useEffect(() => {
        console.log('mount')
        const updateRespawnInterval = setInterval(() => {
            console.log('interval')
            changeMinutesToUpdate();
        }, 1000);
        
        return () => {
            console.log('unmount')
            clearInterval(updateRespawnInterval);
        }
    }, [secondsToUpdate])

    return (
        <Minutes>
            ⟳ {secondsToUpdate}s
        </Minutes>
    );
}

console logs: mount unmount mount

I tried to remove the return, I tried to remove the dependency array or leave it empty. I also tried to move all the functionality to the parent component and transfer seconds via props - none of this helps.

1

There are 1 answers

10
Artsiom On

Your component:

import { useState, useEffect } from "react";

interface IMinutesRemainingProps {
  nextUpdateDateTime: string;
}

function MinutesRemaining({ nextUpdateDateTime }: IMinutesRemainingProps) {
  const [secondsToUpdate, setSecondsToUpdate] = useState<number>(() => {
    //add currentDateTime to have right calculation from current time
    const currentDateTime = new Date().getTime();
    const nextUpdate = new Date(nextUpdateDateTime).getTime();
    return Math.round((nextUpdate - currentDateTime) / 1000);
  });

  useEffect(() => {
    const updateRespawnInterval = setInterval(() => {
      changeMinutesToUpdate();
    }, 1000);

    return () => {
      clearInterval(updateRespawnInterval);
    };
  }, []);

  const changeMinutesToUpdate = () => {
    setSecondsToUpdate((prevSeconds) => {
       //decrease by 1 each second
      if (prevSeconds > 0) {
        return prevSeconds - 1;
      } else {
        return 0;
      }
    });
  };

  return <div>⟳ {secondsToUpdate}s</div>;
}

export default MinutesRemaining;

Set a property:

<MinutesRemaining nextUpdateDateTime="2024-01-22T12:00:00" />