I am building a stopwatch in which there is a timer and the timer should run save the time to asyncstorage to be able to retrieve it and resume from where the timer reached when the app is killed then relaunched. So the problem is that the time is being saved but when i try to retrieve it, the value is 0 and not the saved value. I need your help please to be able to retrieve the right value and if i am saving it right. This way of saving i got from building a previous app which is working perfect but not in the app i am building now
I am testing on Expo Go app on my physical real phone
here is my code:
import React, { useState, useRef, useEffect, useCallback } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Image, Modal, Platform } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
export default function StopwatchScreen() {
const [menuVisible, setMenuVisible] = useState(false);
const [backgroundColor, setBackgroundColor] = useState('#11167F');
const [headerBackgroundColor, setHeaderBackgroundColor] = useState('#0a1055');
const [isRunning, setIsRunning] = useState(false);
const [time, setTime] = useState(0);
const [retrievedTime, setRetrievedTime] = useState(0);
const intervalRef = useRef(null);
const handleLapButtonPress = useCallback(() => {
if(isRunning) {
} else {
setTime(0);
setRetrievedTime(0);
saveAsync('time', 0);
console.log("cleared time: ", time);
console.log("cleared retrieved time: ", retrievedTime);
}
}, [isRunning]);
const handleStartButtonPress = async () => {
setIsRunning(!isRunning);
saveAsync('isRunning', !isRunning);
saveAsync('time', time);
};
useEffect(() => {
if (isRunning) {
console.log("it is running");
intervalRef.current = setInterval(() => {
if(retrievedTime > 0) {
console.log("retrieved > 0");
setTime(previousTime => retrievedTime + previousTime + 100);
} else {
console.log("retrieved not > 0");
setTime(previousTime => previousTime + 100);
}
}, 100);
} else {
clearInterval(intervalRef.current);
console.log("it stopped");
}
return () => clearInterval(intervalRef.current);
}, [isRunning]);
useEffect(() => {
saveAsync('time', time);
}, [time]);
const toggleMenu = () => {
setMenuVisible(!menuVisible);
}
const displayTime = () => {
const milliseconds = parseInt((time%1000)/10);
const centiseconds = Math.floor(milliseconds/10);
const seconds = parseInt((time/1000)%60);
const minutes = parseInt((time/(1000*60))%60)
const hours = parseInt((time/(1000*60*60))%24);
return {
hours: pad(hours),
minutesSeconds: pad(minutes % 60) + ':' + pad(seconds % 60),
centiseconds: centiseconds,
};
};
const pad = (number) => {
return number < 10 ? '0' + number : number;
};
const retrieveAsync = async (key) => {
try {
const jsonValue = await AsyncStorage.getItem(key);
if (jsonValue !== null) {
const parsedValue = JSON.parse(jsonValue);
console.log("parsedValue: ", parsedValue);
if(key === 'time') {
setRetrievedTime(parsedValue);
console.log("retrieve "+key+" inside: ", retrievedTime);
}
}
} catch (e) {
// error reading value
console.log('error: ', e);
}
};
const saveAsync = async (key, value) => {
AsyncStorage.setItem(key, JSON.stringify(value));
console.log(key+" value saved: ", value);
};
useEffect(() => {
console.log("Real retrieved time: ", retrievedTime);
setTime(previousTime => previousTime + retrievedTime);
}, [retrievedTime]);
useEffect(() => {
retrieveAsync('time');
console.log("useEffect retrieved time: ", retrievedTime);
}, []);
return (
<View style={[styles.container, Platform.OS === 'android' && styles.androidPadding]}>
<View style={[styles.header, {backgroundColor: headerBackgroundColor}]}>
<Text style={[styles.title, styles.textFont]}>Futuristic Stopwatch</Text>
<TouchableOpacity onPress={toggleMenu}>
<Image source={require('../assets/images/three_dots.png')} style={styles.dotsIcon} />
</TouchableOpacity>
</View>
<Modal
transparent={false}
animationType="slide"
visible={menuVisible}
onRequestClose={() => setMenuVisible(false)}
>
<View style={styles.modalContainer}>
Modal
</View>
</Modal>
<View style={[styles.contentContainer, {backgroundColor: backgroundColor}]}>
<View style={styles.rowHud}>
<Text style={[styles.hoursTimerOverlay, styles.textFont]}>{`${displayTime(time).hours}`}</Text>
<Text style={[styles.timerOverlay, styles.textFont]}>{`${displayTime(time).minutesSeconds}`}</Text>
<Text style={[styles.centisecondsTimerOverlay, styles.textFont]}>{`${displayTime(time).centiseconds}`}</Text>
</View>
<View style={styles.controls}>
<TouchableOpacity onPress={handleStartButtonPress} style={[styles.controlButtonBorder, { backgroundColor: isRunning ? "#340e0d" : "#0a2a12" }]}>
<View style={styles.controlButton}>
<Text style={{ color: isRunning ? "#ea4c49" : "#37d05c" }}>{isRunning ? 'Stop' : 'Start'}</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={handleLapButtonPress} style={[styles.controlButtonBorder, { backgroundColor: isRunning ? "#333333" : "#1c1c1e" }]}>
<View style={styles.controlButton}>
<Text style={{ color: isRunning ? "#fff" : "#9d9ca2" }}>{isRunning ? 'Lap' : 'Reset'}</Text>
</View>
</TouchableOpacity>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
padding: 0,
},
androidPadding: {
paddingTop: Platform.OS === 'android' ? 25 : 0,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 0,
paddingTop: 18,
paddingBottom: 15,
paddingLeft: 10,
paddingRight: 10,
},
title: {
fontSize: 18,
//fontWeight: 'bold',
color: 'white',
},
dotsIcon: {
width: 20,
height: 20,
resizeMode: 'contain',
},
modalContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
textFont: {
fontFamily: 'Orbitron Black',
},
modalContent: {
backgroundColor: '#fff',
padding: 20,
borderRadius: 10,
elevation: 5,
},
closeButton: {
position: 'absolute',
top: 10,
right: 10,
},
sectionTitle: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 10,
},
sectionTitleText: {
fontFamily: 'Orbitron Black',
},
radioBox: {
marginTop: 20,
alignItems: 'flex-start',
width: 300,
},
radioOptions: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 10,
},
contentContainer: {
flex: 1,
},
rowHud: {
flex: 0.6,
flexDirection: 'row',
marginBottom: 0,
marginTop: 10
},
timerOverlay: {
position: 'absolute',
top: '50%', // Adjust as needed
left: '50%', // Adjust as needed
transform: [{ translateX: -90 }, { translateY: -30 }],
fontSize: 50,
color: 'white',
},
hoursTimerOverlay: {
position: 'absolute',
top: '35%', // Adjust as needed
left: '50%', // Adjust as needed
transform: [{ translateX: -30 }, { translateY: -30 }],
fontSize: 35,
color: 'white',
},
centisecondsTimerOverlay: {
position: 'absolute',
top: '71%', // Adjust as needed
left: '50%', // Adjust as needed
transform: [{ translateX: -15 }, { translateY: -35 }],
fontSize: 35,
color: 'white',
},
controls: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingLeft: 20,
paddingRight: 20,
},
controlButtonBorder: {
justifyContent: "center",
alignItems: "center",
width: 70,
height: 70,
borderRadius: 70,
},
controlButton: {
justifyContent: "center",
alignItems: "center",
width: 65,
height: 65,
borderRadius: 65,
borderColor: "#000",
borderWidth: 1
},
image: {
width: '100%',
height: '100%',
paddingLeft: 10,
paddingRight: 10
}
});
2
Answers
When you retrieve the time from AsyncStorage, set the time state with that value.
Adjust how the time is added when the stopwatch is running. The addition of the previous time and retrieved time might be creating inaccuracies.
kindly modify as follows:
kindly let me know if this helps