los pollos hermanos
los pollos hermanos

Reputation: 337

Why does SetTimeout code still executes after clearTimeout

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?

Upvotes: 3

Views: 3512

Answers (2)

user15045569
user15045569

Reputation:

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

Upvotes: 3

Naren
Naren

Reputation: 4470

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

Upvotes: 0

Related Questions