无法在React本机应用程序中清除setInterval [英] Unable to clear setInterval in react native app
问题描述
我有一个非常简单的应用程序,正在尝试实现倒数计时,开始
和 Stop
功能似乎运行良好,但是当我按 Stop
, seconds
的值不会在视图中更新,这是理想的行为,但是当我观察到控制台日志时,它显示了 sec
的值不断变化,猜测表明 setInterval
仍在运行且未清除.
I have this very simple app, where I am trying to implement countdown, Start
and Stop
functionality seems to be working fine but when I press Stop
, the value of seconds
is not updating in the view which is desired behaviour but when I observed console log, it it showing the value of sec
continuously changing which I guess shows that setInterval
is still running and not cleared.
这是随附的代码:
import React, { useState, useEffect } from "react";
import {
StyleSheet,
Text,
View,
StatusBar,
TouchableOpacity,
Dimensions,
} from "react-native";
const screen = Dimensions.get("screen");
export default function App() {
const [seconds, setSeconds] = useState(4);
const [start, setStartToggle] = useState(true);
let [fun, setFun] = useState(null);
const getRemaining = () => {
const minute = Math.floor(seconds / 60);
const second = seconds - minute * 60;
return formatTime(minute) + ":" + formatTime(second);
};
const formatTime = (time) => {
return ("0" + time).slice(-2);
};
const startTimer = () => {
setStartToggle(false);
console.log("StartTimer");
let sec = seconds;
setFun(
setInterval(() => {
console.log("akak:", sec);
if (sec <= 0) stopTimer();
setSeconds(sec--);
}, 1000)
);
};
const stopTimer = () => {
setStartToggle(true);
console.log("StopTimer");
clearInterval(fun);
setFun(null);
};
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<Text style={styles.timerText}>{getRemaining(seconds)}</Text>
{start ? (
<TouchableOpacity onPress={startTimer} style={styles.button}>
<Text style={styles.buttonText}>Start</Text>
</TouchableOpacity>
) : (
<TouchableOpacity
onPress={stopTimer}
style={[styles.button, { borderColor: "orange" }]}
>
<Text style={styles.buttonText}>Stop</Text>
</TouchableOpacity>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#07121B",
alignItems: "center",
justifyContent: "center",
},
button: {
borderWidth: 10,
borderColor: "#89aaff",
width: screen.width / 2,
height: screen.width / 2,
borderRadius: screen.width / 2,
justifyContent: "center",
alignItems: "center",
},
buttonText: {
fontSize: 40,
color: "#89aaff",
fontWeight: "bold",
},
timerText: {
fontSize: 90,
color: "#89aaff",
fontWeight: "bold",
marginBottom: 20,
},
});
应用行为:
即使在按下Stop后仍显示输出:
Output being shown even after Stop is hit:
感谢 Emiel Zuurbier
和 lissettdm
的回答,我的代码正在运行.
Thanks to Emiel Zuurbier
and lissettdm
for their answers, my code is working now.
工作代码:
// import { StatusBar } from "expo-status-bar";
import React, { useState, useEffect, useRef } from "react";
import {
StyleSheet,
Text,
View,
StatusBar,
TouchableOpacity,
Dimensions,
} from "react-native";
const screen = Dimensions.get("screen");
export default function App() {
const [seconds, setSeconds] = useState(11);
const funRef = useRef(null);
const [start, setStartToggle] = useState(true);
const getRemaining = () => {
const minute = Math.floor(seconds / 60);
const second = seconds - minute * 60;
return formatTime(minute) + ":" + formatTime(second);
};
const formatTime = (time) => {
return ("0" + time).slice(-2);
};
let sec = seconds;
useEffect(() => {
// let timer = null;
if (!start) {
let sec = seconds;
funRef.current = setInterval(() => {
console.log("Seconds remaining:", sec);
if (sec <= 0) {
clearInterval(funRef.current);
setStartToggle(true);
}
setSeconds(sec--);
}, 1000);
} else {
clearInterval(funRef.current);
}
}, [start]);
const startTimer = () => {
setSeconds(sec);
setStartToggle(false);
};
const stopTimer = () => {
setSeconds(sec);
setStartToggle(true);
};
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<Text
style={
seconds ? styles.timerText : [styles.timerText, { color: "red" }]
}
>
{getRemaining(seconds)}
</Text>
{start ? (
<TouchableOpacity onPress={startTimer} style={styles.button}>
<Text style={styles.buttonText}>Start</Text>
</TouchableOpacity>
) : (
<TouchableOpacity
onPress={stopTimer}
style={[styles.button, { borderColor: "orange" }]}
>
<Text style={styles.buttonText}>Stop</Text>
</TouchableOpacity>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#07121B",
alignItems: "center",
justifyContent: "center",
},
button: {
borderWidth: 10,
borderColor: "#89aaff",
width: screen.width / 2,
height: screen.width / 2,
borderRadius: screen.width / 2,
justifyContent: "center",
alignItems: "center",
},
buttonText: {
fontSize: 40,
color: "#89aaff",
fontWeight: "bold",
},
timerText: {
fontSize: 90,
color: "#89aaff",
fontWeight: "bold",
marginBottom: 20,
},
});
推荐答案
当前,您使用 useState
挂钩存储间隔参考.每次重新渲染 App
组件时,都会重置 fun
状态,但不会与间隔完全相同.
Currently you use the useState
hook to store the interval reference. At every re-render of the App
component the fun
state is reset, but will not be the exact same reference to the interval.
代替使用 useRef
钩子.它可以创建对间隔的引用,该间隔在重新渲染期间不会改变.这意味着引用的 current
属性中的值将始终是完全相同的.
Instead use the useRef
hook. It can create a reference to the interval which will not change during re-renders. This means that the value in the current
property of the reference will always be the exact same one.
最重要的是,使用 useEffect
钩子来监视何时设置或取消设置运行状态,并根据该状态启动和停止计时器.
To top it off, use the useEffect
hook to watch when a running state is set or unset and start and stop the timer based on that state.
import React, { useState, useRef, useEffect } from 'react'
export default function App() {
const [isRunning, setIsRunning] = useState(false);
const funRef = useRef(null);
const startTimer = () => {
if (!isRunning) {
setIsRunning(true);
}
};
const stopTimer = () {
if (isRunning && funRef.current !== null) {
setIsRunning(false);
}
};
useEffect(() => {
if (isRunning) {
funRef.current = setInterval(() => { // Save reference to interval.
// ...
}, 1000);
} else {
clearInterval(funRef.current); // Stop the interval.
}
}, [isRunning]);
// ...
}
这篇关于无法在React本机应用程序中清除setInterval的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!