[英]clearInterval() not working in React Native functional component
我有一个屏幕组件,它有一个getPosition()
function 每秒调用一个间隔。
如果stopRace()
function 或者如果用户按下物理/图形后退按钮,我想清除此间隔,使其不会继续在后台运行。
为此,我尝试将区间 ID 存储在raceUpdateInterval
state 变量中。
然后我在stopRace()
function 和cleanup()
function 中使用clearInterval(raceUpdateInterval)
清除此间隔。
当我调用stopRace()
function,然后按回,间隔被清除。 我知道这一点是因为我的控制台日志:
Still Running
Still Running
Still Running
Reached cleanup function
但是,如果我按下后退按钮,则间隔不会清除。 相反,我的控制台记录:
Still Running
Still Running
Still Running
Reached cleanup function
Still Running
随后是 memory 泄漏警告,其中包含以下建议:
To fix, cancel all subscriptions and asynchronous tasks in %s.%s, a useEffect cleanup function
这正是我想要做的,但由于某种超出我理解的原因无法正常工作。
以下是该组件的相关代码:
const RaceScreen = ({route, navigation}) => {
const [raceUpdateInterval, setRaceUpdateInterval] = useState(0);
useEffect(function() {
return function cleanup() {
console.log('Reached cleanup function')
clearInterval(raceUpdateInterval)
}
}, []);
function getPosition(){
console.log("Still being called")
//get position
}
function startRace(){
setRaceUpdateInterval(setInterval(getPosition, 1000))
}
function stopRace(){
clearInterval(raceUpdateInterval)
}
为什么stopRace()
function 正确清除间隔但cleanup()
function 没有?
您的代码可能无法按原样运行的部分原因是,如果您多次运行startRace
function 而不在其间停止它,则间隔将再次启动,但间隔 ID 会丢失。
它未能清除的主要原因是,当使用 [] 作为依赖数组的 useEffect 看到时,它在开始时看到的 raceUpdateInterval 是: 0
。 它没有看到更新值的原因是因为 useEffect 在它运行(和重新运行)的点创建了一个闭包。 所以你需要使用一个引用来让它访问最新版本的 raceUpdateInterval
这是我将如何修改您的代码以使其正常工作的方法。 与其在 function 中启动计时器,不如使用useEffect
启动该副作用,这样就永远不会出现计时器无法清理的情况。
我使用 ref 将 function 添加到区间,因为我不知道 getPosition function 中有多少闭包变量。 这样 positionFunctRef.current 总是指向最新版本的 function 而不是剩余的 static。
const RaceScreen = ({ route, navigation }) => {
const [runningTimer, setRunningTimer] = useState(false);
function getPosition() {
console.log('Still being called');
//get position
}
const positionFunctRef = useRef(getPosition);
useEffect(() => {
positionFunctRef.current = positionFunctRef;
});
useEffect(
function () {
if (!runningTimer) {
return;
}
const intervalId = setInterval(() => {
positionFunctRef.current();
}, 1000);
return () => {
console.log('Reached cleanup function');
clearInterval(intervalId);
};
},
[runningTimer]
);
function startRace() {
setRunningTimer(true);
}
function stopRace() {
setRunningTimer(false);
}
};
componentWillUnmount
用于清理(如删除事件侦听器、取消计时器等)。 假设您在componentWillUnmount
中添加一个事件侦听器,并在componentDidMount
中删除它,如下所示。
componentDidMount() {
window.addEventListener('mousemove', () => {})
}
componentWillUnmount() {
window.removeEventListener('mousemove', () => {})
}
上面代码的钩子等效项如下
useEffect(() => {
window.addEventListener('mousemove', () => {});
// returned function will be called on component unmount
return () => {
window.removeEventListener('mousemove', () => {})
}
}, [])
所以你更好的代码是:
useEffect(function() {
setRaceUpdateInterval(setInterval(getPosition, 1000))
return function cleanup() {
console.log('Reached cleanup function')
clearInterval(raceUpdateInterval)
}
}, []);
不要在 state 中存储间隔 id 之类的东西,因为每次更新都会重新渲染。 如果您功能正常,请使用setInterval
实现useRef()
,如果基于 class ,请使用this.interval
。
另一个问题是在 ref 的功能组件中调用clearInterval()
,而不是.current
这是我刚刚调试的一个片段:
const spinnerCount = useRef(0)
const interval = useRef(null)
useEffect(() => {
if (withProgress && inProgress && notification == '') {
interval.current = setInterval(() => {
if (spinnerCount.current >= 40) {
clearInterval(interval.current)
spinnerCount.current = 0
setNotification('Something happened... Please try again.')
} else {
spinnerCount.current = spinnerCount.current + 1
}
}, 1000)
}
if (notification !== '' && inProgress === false) {
const delay = notification.length > 100 ? 6000 : 3000
setTimeout(() => {
clearInterval(interval.current)
spinnerCount.current = 0
setNotification('');
}, delay);
}
}, [inProgress])
里面有一些额外的东西,基本上这是一个消失的通知组件,它还具有一个进度微调器。 在这种情况下,如果组件正在显示微调器,但从未触发成功/错误通知,则微调器将在 40 秒后自动退出。 因此间隔/微调器计数
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.