skip to Main Content

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


  1. 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.

    // ... (other imports and styles)
    
    export default function StopwatchScreen() {
        // ... (existing code remains the same)
    
        useEffect(() => {
            retrieveAsync('time');
        }, []);
    
        useEffect(() => {
            if (isRunning) {
                intervalRef.current = setInterval(() => {
                    setTime(previousTime => previousTime + 100);
                }, 100);
            } else {
                clearInterval(intervalRef.current);
            }
    
            return () => clearInterval(intervalRef.current);
        }, [isRunning]);
    
        const retrieveAsync = async (key) => {
            try {
                const jsonValue = await AsyncStorage.getItem(key);
    
                if (jsonValue !== null) {
                    const parsedValue = JSON.parse(jsonValue);
                    if (key === 'time') {
                        setTime(parsedValue); // Set the retrieved time here
                        setRetrievedTime(parsedValue);
                    }
                }
            } catch (e) {
                console.log('error: ', e);
            }
        };
    
        const handleStartButtonPress = () => {
            setIsRunning(!isRunning);
            saveAsync('isRunning', !isRunning);
        };
    
        const handleLapButtonPress = () => {
            if (isRunning) {
                // Do something for lap when running
            } else {
                setTime(0);
                saveAsync('time', 0); // Reset time when stopped
            }
        };
    
        // ... (rest of the existing code remains the same)
    }
    
    
    Login or Signup to reply.
  2. kindly modify as follows:

    // ...
    
    const retrieveAsync = async (key) => {
        try {
            const jsonValue = await AsyncStorage.getItem(key);
    
            if (jsonValue !== null) {
                const parsedValue = JSON.parse(jsonValue);
                console.log("parsedValue for " + key + ": ", parsedValue);
    
                if (key === 'time') {
                    setRetrievedTime(parsedValue);
                    console.log("retrieve " + key + " inside: ", parsedValue);
                }
            }
        } catch (e) {
            // error reading value
            console.log('error: ', e);
        }
    };
    
    const saveAsync = async (key, value) => {
        await AsyncStorage.setItem(key, JSON.stringify(value));
        console.log(key + " value saved: ", value);
    };
    
    // ...
    
    useEffect(() => {
        retrieveAsync('time');
        console.log("useEffect retrieved time: ", retrievedTime);
    }, []);
    
    useEffect(() => {
        console.log("Real retrieved time: ", retrievedTime);
        setTime(retrievedTime);
    }, [retrievedTime]);
    
    useEffect(() => {
        saveAsync('time', time);
    }, [time]);
    
    // ...
    

    kindly let me know if this helps

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search