I am working on a running tracker app using react-native. I fetch user's location every 5 meters and then store their lat and long inside a route array. When the user ends the run I want to store the array with all the points inside AsyncStorage. the problem is that when it stores an empty array.
this is what it stores. I'm not sure why. Any help is appreciated.
Object {
"route": Array [],
"speed": 0,
"totalDistance": 0,
}
My code
github link for full project. https://github.com/jasingh784/runningTrackerv2
import React, {useState, useEffect} from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import * as Location from 'expo-location';
import { getDistance } from '../utils';
import Timer from './Timer';
import { storeRun } from '../utils';
export default function NewRun() {
const [route, setRoute] = useState([]);
const [errorMsg, setErrorMsg] = useState(null);
const [speed, setSpeed] = useState(0);
const [totalDistance, setTotalDistance] = useState(0);
//setup watch position
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
locationSubscription = await Location.watchPositionAsync({accuracy: Location.Accuracy.BestForNavigation, distanceInterval: 5}, locationUpdated);
})();
return () => {
endRun()
locationSubscription.remove();
}
}, []);
//this effect runs every time a new coord is added to the route array.
//it calls a function to calculate the distance between two points. it then adds the
//result to the total distance.
useEffect(() => {
if(route.length >= 2) {
distBetweenLastPoints = getDistance(route[route.length - 1]["latitude"],
route[route.length - 2]["latitude"],
route[route.length - 1]["longitude"],
route[route.length - 2]["longitude"] );
setTotalDistance(totalDistance => totalDistance + distBetweenLastPoints)
}
return ()=> {
//not sure if there is any clean up in this effect. i dont think so.
}
}, [route])
//get location and add entry into the route array. This array contains points
//that can be used to map out the route.
const locationUpdated = (locObject) => {
console.log('inside locationupdated')
//console.log(locObject)
setRoute(oldRoute => [...oldRoute, {
latitude: locObject.coords.latitude,
longitude: locObject.coords.longitude,
}]);
setSpeed(locObject.coords.speed);
}
//this function is called when user presses End Run.
//it puts all the data for the run into one object and
//then stores it in local storage. it only stores the last run
//also remves the location subscription so we app can stop tracking
//location
const endRun = () => {
const run = {route, totalDistance, speed}
console.log(run)
storeRun(run);
locationSubscription.remove();
}
let text = 'Waiting..';
if (errorMsg) {
text = errorMsg;
} else if (route) {
text = JSON.stringify(route);
}
return (
<View style={styles.container}>
<Text style={styles.paragraph}>{Math.round((speed * 2.2369) * 100) / 100} mph</Text>
<Text>Distance Travelled: {route ? totalDistance : 0} miles</Text>
<Timer />
<Button title="End Run" onPress={endRun}/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
code to storeRun
export const storeRun = async (runData) => {
try {
const jsonValue = JSON.stringify(runData)
await AsyncStorage.setItem('previousRun', jsonValue)
} catch (e) {
// saving error
console.log(e);
}
console.log('added previous run to local storage');
}
ok. so I found a workaround to my problem. I made a new function and put the code to store items in there. then i made a button and called that function when button gets pressed. I'm not sure why the same code wont work in the clean up of my useffect hook. It could be that my component gets unmounted before the code finished to run??? I dont know enough about react yet.