Hello i just started developing mobile apps with react native and expo. Im making a simple timer app which is supposed to count down to 0 and you can pause, resume and stop the timer. I am currently stuck on a problem though and am unable to figure out what the problem is.
Here is a short video of the problem, the timer counts down like its supposed to but the resumeTimer() function doesnt seem to update the state correctly and still asumes it has the "resume" state even though it has already been switched to something else. Same thing with the secondsLeft state which is reset each time i set it to 0.
Here is the code of the component:
// Imports //
import {
Platform,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import { useState } from "react";
import Ionicons from "react-native-vector-icons/Ionicons";
import { AnimatedCircularProgress } from "react-native-circular-progress";
import TimeConverter from "../modules/TimeConverter";
import DateTimePicker from "@react-native-community/datetimepicker";
export default function Timer() {
// State //
const startingSeconds = 22;
const [secondsLeft, setSecondsLeft] = useState(startingSeconds);
const [state, setState] = useState("pause");
// Functions //
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
const resumeTimer = async (secLeft) => {
if (state == "resume" && secLeft > 0) {
await sleep(1000);
if (state == "resume") {
setSecondsLeft(secLeft - 1);
resumeTimer(secLeft - 1);
}
}
};
const pauseTimer = () => {
setState("pause");
};
const stopTimer = () => {
setState("stop");
setSecondsLeft(0);
};
return (
<View style={styles.container}>
<View style={styles.topBar}>
<Text style={styles.topBarTitle}>State: {state}</Text>
</View>
<View style={styles.timerContainer}>
<AnimatedCircularProgress
style={{ position: "absolute" }}
size={300}
width={5}
fill={(secondsLeft / startingSeconds) * 100}
tintColor="red"
onAnimationComplete={() => console.log("onAnimationComplete")}
backgroundColor="white"
/>
<TouchableOpacity disabled={state == "resume" ? true : false}>
<View style={styles.timerCircle}>
<Text style={styles.timerText}>
{TimeConverter.secondsToTimerString(secondsLeft)}
</Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.bottomBar}>
<TouchableOpacity>
<Ionicons
style={styles.bottomBarItem}
name={"pause-outline"}
size={60}
color={"white"}
onPress={() => {
pauseTimer();
}}
/>
</TouchableOpacity>
<TouchableOpacity>
<Ionicons
style={styles.bottomBarItem}
name={"play-outline"}
size={60}
color={"white"}
onPress={() => {
setState("resume");
resumeTimer(secondsLeft);
}}
/>
</TouchableOpacity>
<TouchableOpacity>
<Ionicons
style={styles.bottomBarItem}
name={"stop-outline"}
size={60}
color={"white"}
onPress={() => {
stopTimer();
}}
/>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "space-between",
backgroundColor: "#423D6A",
},
topBar: {
flex: 0.13,
backgroundColor: "#423D6A",
alignItems: "center",
justifyContent: "flex-end",
borderColor: "white",
borderBottomWidth: 1,
borderBottomLeftRadius: 25,
borderBottomRightRadius: 25,
},
topBarTitle: {
fontSize: 30,
color: "white",
marginBottom: 10,
},
bottomBar: {
flex: 0.2,
backgroundColor: "#423D6A",
flexDirection: "row",
borderTopLeftRadius: 25,
borderTopRightRadius: 25,
justifyContent: "space-evenly",
alignItems: "center",
borderColor: "white",
borderTopWidth: 1,
},
bottomBarItem: {
marginBottom: "5%",
},
timerContainer: {
flex: 0.67,
marginTop: "10%",
justifyContent: "center",
alignItems: "center",
},
timerCircle: {
width: 250,
height: 250,
borderRadius: 250 / 2,
backgroundColor: "pink",
justifyContent: "center",
alignItems: "center",
},
timerText: {
fontSize: 40,
},
});
I dont even really know where to start debugging because i dont understand the problem. Im hoping to get an explanation on why it doesnt behave as I expected it to.
2
Answers
Update this line
try to update the stop method like this: