Home > database >  How can I start / stop setInterval?
How can I start / stop setInterval?

Time:12-11

I've tried different ways, but It doesn't works.

[...]
  const [automatic, setAutomatic] = useState(false);

[...]
  var startAuto;

  useEffect(() => {
    if (!automatic) {
      console.log("stop");
      clearInterval(startAuto);
    } else {
      startAuto = setInterval(() => {
        changeQuestion(" ");
      }, 5 * 1000);
    }
  }, [automatic]);

[...]
        <Button
          onPress={() => setAutomatic(!automatic)}
          title="turn on/off"
        />
[...]

It works when I put a setTimeout outside the useEffect, that way:

setTimeout(() => { clearInterval(startAuto); alert('stop'); }, 10000);

But I want to have a button to start / stop

CodePudding user response:

Your var startAuto; is redeclared on each render, and since changing the state causes a re-render, it never holds the reference to the interval, which is never cleared.

Use the useEffect cleanup function to clear the interval. Whenever automatic changes, it would call the cleanup (if returned by the previous invocation), and if automatic is true it would create a new interval loop, and return a new cleanup function of the current interval.

useEffect(() => {
  if(!automatic) return;
  
  const startAuto = setInterval(() => {
    changeQuestion(" ");
  }, 5 * 1000);

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

Working example:

const { useState, useEffect } = React;

const Demo = () => {
  const [automatic, setAutomatic] = useState(false);
  const [question, changeQuestion] = useState(0);
  
  useEffect(() => {
    if(!automatic) return;
    
    const startAuto = setInterval(() => {
      changeQuestion(q => q   1);
    }, 5 * 100);

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

  return (
    <div>
      <button
        onClick={() => setAutomatic(!automatic)}
      >
        turn {automatic ? 'off' : 'on'}
      </button>
      
      <p>{question}</p>
    </div>
  );
}

ReactDOM
  .createRoot(root)
  .render(<Demo />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>

CodePudding user response:

For example, you can check and use this hook:

https://usehooks-ts.com/react-hook/use-interval

export default function Component() {
  // The counter
  const [count, setCount] = useState<number>(0)
  // Dynamic delay
  const [delay, setDelay] = useState<number>(1000)
  // ON/OFF
  const [isPlaying, setPlaying] = useState<boolean>(false)

  useInterval(
    () => {
      // Your custom logic here
      setCount(count   1)
    },
    // Delay in milliseconds or null to stop it
    isPlaying ? delay : null,
  )

  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    setDelay(Number(event.target.value))
  }

  return (
    <>
      <h1>{count}</h1>
      <button onClick={() => setPlaying(!isPlaying)}>
        {isPlaying ? 'pause' : 'play'}
      </button>
      <p>
        <label htmlFor="delay">Delay: </label>
        <input
          type="number"
          name="delay"
          onChange={handleChange}
          value={delay}
        />
      </p>
    </>
  )
}
  • Related