Search code examples
javascripttypescriptsettimeoutcleartimeout

Cannot stop setTimeout function using clearTimeout because value is null for some reason


In my react-native app, I'm trying to stop setTimeout using clearTimeout. I save an instance of the setTimeout in a global variable.

let timeoutId:any = null;

    const doOtp = ()=>{
        if(canSendOtp) {
            setCanSendOtp(false);

            timeoutId = setTimeout(() => { // it has here a numeric value
                showNotificationMessage("You can request OTP again")
                setCanSendOtp(true)
            }, SEND_OTP_TIME_CONSTRAINTS)

           // rest of doOtp logic
        }
        else {
            showNotificationMessage("Please wait " + (SEND_OTP_TIME_CONSTRAINTS / 1000) + " seconds before trying again")
        }
    }

Then when I want to stop the setTimeout using clearTimeout, I see that the value of timeoutId is null. I don't understand why it's happening.

const doLogin = () => {
issueToken(LOGIN_GRANT_TYPE, LOGIN_CLIENT_ID, LOGIN_CLIENT_SECRET, phoneNumber, otp)
    .then(res => { 
        
        console.log('timeoutId !== null' + timeoutId !== null)
        if(timeoutId !== null) { // value here is null - why?
            clearTimeout(timeoutId)
        }

        store().dispatch(setTokenValidity(res))
    })
    .catch(err => {
        showNotificationMessage('Error, something went wrong check logs.')
        console.log("issueToken error: " + JSON.stringify(err))
    });

}


Solution

  • PROBLEM

    setCanSendOtp(true) updates your state which initializes your timeout to null again.

    SOLUTION

    Put your timeout in Ref. Ref values are persistent across re-renders and state-updates.

    const timeoutId:any = React.useRef(null);
    
    const doOtp = ()=>{
            if(canSendOtp) {
                setCanSendOtp(false);
    
                timeoutId.current = setTimeout(() => { // it has here a numeric value
                    showNotificationMessage("You can request OTP again")
                    setCanSendOtp(true)
                }, SEND_OTP_TIME_CONSTRAINTS)
    
               // rest of doOtp logic
            }
            else {
                showNotificationMessage("Please wait " + (SEND_OTP_TIME_CONSTRAINTS / 1000) + " seconds before trying again")
            }
        }
    
    const doLogin = () => {
    issueToken(LOGIN_GRANT_TYPE, LOGIN_CLIENT_ID, LOGIN_CLIENT_SECRET, phoneNumber, otp)
        .then(res => { 
            
            if(timeoutId.current !== null) {
                clearTimeout(timeoutId.current)
            }
    
            store().dispatch(setTokenValidity(res))
        })
        .catch(err => {
            showNotificationMessage('Error, something went wrong check logs.')
            console.log("issueToken error: " + JSON.stringify(err))
        });