React: ClearInterval and Immediately Start Again

Viewed 166

I have a component that sets off a timer which updates and makes an axios request every 30 seconds. It uses a useRef which is set to update every 30 seconds as soon as a function handleStart is fired.


  const countRef = useRef(null);
  const lastUpdatedRef = useRef(null);
  const [lastUpdated, setLastUpdated] = useState(Date.now())

  const handleStart = () => {

    countRef.current = setInterval(() => {
      setTimer((timer) => timer + 1);
    }, 1000);

    lastUpdatedRef.current = setInterval(() => {
      setLastUpdated(Date.now());
    }, 30000);
  };

Now I have a useEffect that runs a calculate function every 30 seconds whenever lastUpdated is triggered as a dependency:

  const firstCalculate = useRef(true);

  useEffect(() => {
    if (firstCalculate.current) {
      firstCalculate.current = false;
      return;
    }
    console.log("calculating");
    calculateModel();
  }, [lastUpdated]);

This updates the calculate function every 30 seconds (00:30, 01:00, 01:30 etc.) as per lastUpdatedRef. However, I want the timer to restart from when lastUpdated state has been modified elsewhere (e.g. if lastUpdated was modified at 00:08, the next updated will be 00:38, 01:08, 01:38 etc.). Is there a way to do this?

1 Answers

Basically it sounds like you just need another handler to clear and restart the 30 second interval updating the lastUpdated state.

Example:

const handleOther = () => {
  clearInterval(lastUpdatedRef.current);
  lastUpdatedRef.current = setInterval(() => {
    setLastUpdated(Date.now());
  }, 30000);
}

Full example:

const calculateModel = () => console.log("calculateModel");

export default function App() {
  const countRef = React.useRef(null);
  const lastUpdatedRef = React.useRef(null);
  const [lastUpdated, setLastUpdated] = React.useState(Date.now());
  const [timer, setTimer] = React.useState(0);

  const handleStart = () => {
    countRef.current = setInterval(() => {
      setTimer((timer) => timer + 1);
    }, 1000);

    lastUpdatedRef.current = setInterval(() => {
      setLastUpdated(Date.now());
    }, 30000);
  };

  const handleOther = () => {
    clearInterval(lastUpdatedRef.current);
    lastUpdatedRef.current = setInterval(() => {
      setLastUpdated(Date.now());
    }, 30000);
  };

  const firstCalculate = React.useRef(true);

  React.useEffect(() => {
    if (firstCalculate.current) {
      firstCalculate.current = false;
      return;
    }
    console.log("calculating");
    calculateModel();
  }, [lastUpdated]);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>

      <div>Timer: {timer}</div>

      <button type="button" onClick={handleStart}>
        Start
      </button>
      <button type="button" onClick={handleOther}>
        Other
      </button>
    </div>
  );
}

Edit react-clearinterval-and-immediately-start-again

Don't forget to clear any running intervals when the component unmounts!

React.useEffect(() => {
  return () => {
    clearInterval(countRef.current);
    clearInterval(lastUpdatedRef.current);
  };
}, []);
Related