React中通过loading状态清除setInterval计时器失效问题
问题根源
代码不生效是典型的React闭包陷阱:setInterval的回调在创建时就捕获了当前作用域下的loading值(也就是调用getPrestoData刚执行完setLoading(true)时的true值),后续setLoading(false)触发组件重渲染,根本不会更新这个已经存在的定时器回调里的loading变量,所以if (!loading)的判断永远不会成立,定时器自然不会自动停止。
另外当前写法还有隐患:请求走catch分支时既没有把loading设回false,也没有清除定时器,会进一步导致状态异常。
修复方案
不要在定时器内部依赖state判断停止时机,直接在请求流程结束(无论成功/失败)时手动清除定时器即可,这是最可靠的实现方式:
const [loading, setLoading] = useState(null) const [error, setError] = useState('') // Counter const [count, setCounter] = useState(0) const getPrestoData = async () => { setLoading(true) setCounter(0) // 每次发起新请求前重置计数 let currentCount = 0 // 保存定时器ID const timer = setInterval(() => { currentCount++ setCounter(currentCount) }, 1000) try { const response = await fetch(`http://127.0.0.1:8000/get-dspdaily/${dealId}`) const data = await response.json() setData(data) setDealId('') } catch (err) { console.log(err) setError(err) } finally { // 无论请求成功还是失败,都停止计数、关闭loading状态 clearInterval(timer) setLoading(false) } }
可选优化
如果组件可能在请求过程中被卸载,建议把定时器ID存在useRef中,在组件卸载时主动清除,避免内存泄漏:
import { useState, useRef, useEffect } from 'react' // 组件内新增ref存定时器id const timerRef = useRef(null) const getPrestoData = async () => { setLoading(true) setCounter(0) let currentCount = 0 timerRef.current = setInterval(() => { currentCount++ setCounter(currentCount) }, 1000) try { const response = await fetch(`http://127.0.0.1:8000/get-dspdaily/${dealId}`) const data = await response.json() setData(data) setDealId('') } catch (err) { console.log(err) setError(err) } finally { clearInterval(timerRef.current) setLoading(false) } } // 组件卸载时兜底清定时器 useEffect(() => { return () => { if (timerRef.current) clearInterval(timerRef.current) } }, [])
内容的提问来源于stack exchange,提问作者mrpbennett
相关产品推荐
相关产品推荐

