2

I have following code in my react component:

//some code
var timeout = null; //global variable

//some more code

useEffect(() => {
    if(...some condition) {
        console.log('test1');
        timeout = setTimeout(() => {
                console.log('test2');
        }, 15 * 1000);
    } else {
        console.log('test3');
        clearTimeout(timeout);
    }
}, [some other condition]);

The output I get in the console:

test1
test3
test2 //after 15 seconds

So I am trying to start timeout which will log 'test2', if in the meantime timeout was not cleared. As we can see 'test3' is logged, meaning timeout was cleared, but still 'test2' is logged after 15 seconds. What is the issue here?

  • Having a global variable is not a good idea – NineBerry Jan 21 '21 at 16:34
  • Have you made sure the contents of `timeout` are the same? – Sami Kuhmonen Jan 21 '21 at 16:38
  • Does this answer your question? [Why is clearTimeout not clearing the timeout in this react component?](https://stackoverflow.com/questions/57995978/why-is-cleartimeout-not-clearing-the-timeout-in-this-react-component) – Naren Jan 21 '21 at 16:38
  • 5
    Are you sure `clearTimeout` clears the *correct* timer? Without knowing the condition here, I'd say it's very likely that what happens is 1. timer with ID `1` is created. 2. timer with ID `2` is created. 3. `clearTimeout` runs clearing the last ID `2`. 4. timer with ID `1` runs. – VLAZ Jan 21 '21 at 16:38
  • Move var timeout = null to useEffect – lissettdm Jan 21 '21 at 16:46
  • I think @VLAZ is right. You might try making your timer with a `setState` so it's preserved between renders. – JakeAve Jan 21 '21 at 16:47
  • if you liked the answer try to accept it –  Jan 21 '21 at 17:20
  • Yes @VLAZ, I had correct conditions, the correct answer is provided by Weblandtk. Thanks! – los pollos hermanos Jan 21 '21 at 18:21

2 Answers2

3

You need to save timeout in a ref like this, everytime the component rerenders there is a new instance of timeout and that's why when you clearTimeout it doesn't work, because it clears a wrong timer:

  const timeout = useRef(null); //global variable
  const [test, setTest] = useState(true);

  //some more code

  setTimeout(() => {
    console.log("setting");
    setTest(false);
  }, 2000);
  useEffect(() => {
    if (test) {
      console.log("test1");
      timeout.current = setTimeout(() => {
        console.log("test2");
      }, 5 * 1000);
    } else {
      console.log("test3");
      clearTimeout(timeout.current);
    }
   return () => clearTimeout(timeout.current) // clearing on unmount
  }, [test]);

the code above outputs this:

test1 

setting 

test3 

setting 

you can try it here: https://codesandbox.io/s/boring-waterfall-6f6iv?file=/src/App.js

0

You need a hook cleanup function to handle, this will remove clearTimeout while Unmounting the component and Also you don't need global variable.

Try like this.

  const [test, setTest] = useState(true);
  
  // Trigger change
  setTimeout(() => {
    setTest(false);
  }, 2000);

  useEffect(() => {
    let timeout = null
    console.log("test1")
    if (test) {
      timeout = setTimeout(() => {
        console.log("test2");
      }, 15 * 1000);
    }

    return () => { // Needs cleanup function
      clearTimeout(timeout);
    }

  }, [test]);

Demo link is here

Naren
  • 4,152
  • 3
  • 17
  • 28