关于用clearInterval手动清除定时器无效的问题

1,108 阅读2分钟

背景:

实现答题倒计时的功能:设置答题时间例如10s,用户需要在答题时间内作答,超出答题时间未答,提示用户超时。

定时器的创建和销毁:

定时器创建时期:

页面载入时

首先在函数内定义一个定时器interval,页面加载时开始计时

截屏2022-10-04 下午10.35.30.png

const QuestionPage: React.FC<IQuestionPage> = (props) => {
    let remainTime = 0;
    let interval: any = null;
    useEffect(() => {
        interval = setInterval(() => {
            if (remainTime <= 0) {
                clearInterval(interval)
            } else {
                remainTime--
            }
        }, 1000)
    }, [])
    const chooseAnswer = () => {
        clearInterval(interval)
    }
    return <div onClick={chooseAnswer}>点击销毁定时器</div>
}
export default QuestionPage

定时器需要销毁时期:

1、页面即将销毁

useEffect(() => {
    return () => {
        if (interval) {
            clearInterval(interval)
        }
    }
}, [])

2、倒计时结束

第一个代码块已展示

3、在答题时间内用户点击答案

const chooseAnswer = () => {
    clearInterval(interval)
}

遇到的问题:

倒计时结束,定时器可销毁,但是用户在答题时间内作答,点击销毁定时器未生效,依然在倒计时

解决方法1:

将定时器放在函数的最外层

let remainTime = 0;
let interval: any = null;
const QuestionPage: React.FC<IQuestionPage> = (props) => {
    。。。
}

解决方法2:

将定时器放在状态里

const QuestionPage: React.FC<IQuestionPage> = (props) => {
    const [interval, setinterval] = React.useState(null);
    useEffect(() => {
        let interval = setInterval(() => {
            if (remainTime <= 0) {
                clearInterval(interval)
            } else {
                remainTime--
            }
        }, 1000)
        setinterval(interval)
    }, [])
    const chooseAnswer = () => {
        clearInterval(interval)
    }
    return <div onClick={chooseAnswer}>点击销毁定时器</div>
}
export default QuestionPage

总结:

react用链表的方式来存储函数式组件里面的hooks,并为每一个hook创建了一个对象,在组件更新时会根据链表拿到当前hooks对应的hook对象。

在例子中用useEffect对定时器倒计时,在初始化时渲染一次,此时定时器内回调函数内会形成闭包,倒计时结束后清除定时器会生效。但是在后续外部调用清除定时器的方法时,页面可能已重新渲染多次,interval的值早已被重置。

方法1把定时器设为全局变量,interval不会被重新赋值,所以生效,但是这种方法不推荐,变量放在外部可能会引起冲突。方法2将定时器放进useState,会跟每次渲染一起将状态向后续的hook链表传递,状态被保存了下来,推荐此方法。