我有一个具有getPosition()
功能的屏幕组件,该功能每秒间隔调用一次。
如果调用了stopRace()
函数,或者用户按下了物理/图形后退按钮,则我想清除此间隔,以使其不会继续在后台运行。
为此,我尝试将间隔ID存储在raceUpdateInterval
状态变量中。
然后我使用clearInterval(raceUpdateInterval)
函数和stopRace()
函数中的cleanup()
清除此间隔。
当我调用stopRace()
函数时,然后按back,将清除间隔。我知道这是因为我的控制台记录了:
Still Running
Still Running
Still Running
Reached cleanup function
但是,如果按返回按钮,则间隔不会清除。而是我的控制台日志:
Still Running
Still Running
Still Running
Reached cleanup function
Still Running
紧接着是包含以下建议的内存泄漏警告:
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()
函数可以正确清除间隔,而cleanup()
函数却不能清除间隔?
答案 0 :(得分:1)
您的代码可能无法正常运行的部分原因是,如果您多次运行startRace
函数而不在其间停止,则间隔将再次启动,但间隔ID会重新开始已经迷路了。
无法清除的主要原因是,当useEffect带有[]作为依赖项数组时,它在开始时看到的raceUpdateInterval是:0
。之所以看不到更新后的值,是因为useEffect在运行(和重新运行)的点创建了一个封闭值。因此,您需要使用引用来为其提供对raceUpdateInterval最新版本的访问权限
这是我修改您的代码以使其正常运行的方式。不用在函数中启动计时器,而使用useEffect
来启动该副作用,这样就永远不会出现计时器无法清除的情况。
我使用ref将函数添加到间隔中,因为我不知道getPosition函数中有多少个关闭变量。这样,positionFunctRef.current始终指向该函数的最新版本,而不是保持静态。
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);
}
};
答案 1 :(得分:1)
componentWillUnmount
用于清理(例如删除事件侦听器,取消计时器等)。假设您要在componentDidMount
中添加事件监听器,并在componentWillUnmount
中将其删除,如下所示。
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)
}
}, []);