Hi I'm currently trying to build a running app similar to the UnderArmor running app in React Native. The features work but after a couple of minutes of using the app on the simulator the computer starts to overheat and the app's performance significantly decreases, after a bit it no longer becomes responsive. Am I making too many calculations per frame? Thank for for your help
const { width, height } = Dimensions.get('window')
const LATITUDE_DELTA = 0.007;
const LONGITUDE_DELTA = 0.007;
const LATITUDE = 37.78825;
const LONGITUDE = -122.4324;
var email = "";
const RunMapScreen = () => {
const paperTheme = useTheme();
const [state, setState] = useState({
isActive: false,
close: true,
routeCoordinates: [],
distanceTravelled: 0,
prevLatLng: {},
latitude: LATITUDE,
longitude: LONGITUDE,
seconds: 0,
now: moment(),
then: moment(),
timeElapsed: "00:00:00",
startCounter: 0,
speedCounter: 1,
speed: 0,
averageSpeed: 0,
isModalVisible: false,
email: "grt",
});
const [isModalVisible, setModalVisible] = useState(false);
useEffect(() => {
this.watchID = navigator.geolocation.watchPosition((position) => {
handleUpdates(position, position.coords.latitude, position.coords.longitude, position.coords.speed);
});
return () => {
navigator.geolocation.clearWatch(this.watchID);
}
}, [state]);
const handleUpdates = (position, lat, long, speedVal) => {
const newLatLngs = { latitude: position.coords.latitude, longitude: position.coords.longitude }
const positionLatLngs = _.pick(position.coords, ['latitude', 'longitude']);
setState(state => ({ ...state, latitude: lat, longitude: long }));
if (state.isActive) {
setState(state => ({
...state,
routeCoordinates: state.routeCoordinates.concat(positionLatLngs),
distanceTravelled: state.distanceTravelled + calcDistance(newLatLngs),
prevLatLng: newLatLngs,
now: moment(),
timeElapsed: moment.utc(moment(state.now, "DD/MM/YYYY HH:mm:ss").diff(moment(state.then, "DD/MM/YYYY HH:mm:ss"))).format("HH:mm:ss"),
speedCounter: state.speedCounter + 1,
speed: speedVal,
averageSpeed: ((state.averageSpeed * (state.speedCounter - 1) + state.speed) / state.speedCounter),
}));
}
};
const calcDistance = (newLatLng) => {
const prevLatLng = state.prevLatLng;
return (haversine(prevLatLng, newLatLng) || 0);
};
const openIsActive = () => {
var now;
if (!state.isActive && state.startCounter === 0) {
setState(state => ({
...state,
timeElapsed: moment.duration(state.now.diff(state.then)),
then: moment(),
startCounter: 1
}));
} else if (state.isActive && state.startCounter === 1) {
now = { ...state.now };
} else if (!state.isActive && state.startCounter === 1) {
var then = { ...state.then };
var diff = -state.now.diff(now);
setState(state => ({ ...state, then: moment(then).add(diff) }));
}
setState(state => ({ ...state, isActive: !state.isActive }));
}
const saveData = () => {
firebase.auth().onAuthStateChanged((user) => {
var ref = firebase.database().ref(user.email.replace('.', ''));
var key = firebase.database().ref(ref).push().key;
firebase.database().ref(ref).child(key).set({
email: user.email,
distance: state.distanceTravelled,
time: state.timeElapsed,
speed: state.speed,
averageSpeed: state.averageSpeed
});
});
setState(state => ({
...state,
isActive: false,
routeCoordinates: [],
distanceTravelled: 0,
prevLatLng: {},
seconds: 0,
now: moment(),
then: moment(),
timeElapsed: "00:00:00",
startCounter: 0,
speedCounter: 1,
speed: 0,
averageSpeed: 0,
}));
navigator.geolocation.clearWatch(watchID);
}));
}
const endRun = () => {
if (state.isActive) {
setState(state => ({ ...state, isActive: false }));
Alert.alert(
"End Run",
"Do you want to end the run?",
[{
text: "Cancel",
onPress: () => { setState(state => ({ ...state, isActive: true })) },
style: "cancel"
},
{
text: "OK",
onPress: () => saveData()
}], { cancelable: false });
} else {
Alert.alert(
"Error",
"You cannot end run that hasn't started",
[{
text: "OK",
style: "cancel"
}], { cancelable: false });
}
}
const toggleModal = () => {
setState(state => ({ ...state, isModalVisible: !state.isModalVisible }));
};
useEffect(() => {
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 15000,
})
.then(pos => {
setState(state => ({ ...state, latitude: pos.latitude, longitude: pos.longitude }));
})
.catch(error => {
const { code, message } = error;
console.log(code, message);
});
});
const getMapRegion = () => ({
latitude: state.latitude,
longitude: state.longitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA
});
const { colors } = useTheme();
const theme = useTheme();
return (
<View style={styles.container}>
<Map
routeCoordinates={state.routeCoordinates}
getRegion={getMapRegion()} />
<Button title="Show modal" onPress={toggleModal} />
<Modal testID={'modal'}
isVisible={state.isModalVisible}
onSwipeComplete={() => { setModalVisible(false) }}
swipeDirection={['up', 'left', 'right', 'down']}
style={styles.modalView}>
<ModalView
timeElapsed={state.timeElapsed}
distanceTravelled={state.distanceTravelled}
speed={state.speed}
averageSpeed={state.averageSpeed}
toggleModal={toggleModal} />
</Modal>
<FloatingButton
openIsActive={openIsActive}
endRun={endRun}
toggleModal={toggleModal}
style={{ bottom: 100 }} />
</View>
);
};
export default RunMapScreen;
The map component
const { width, height } = Dimensions.get('window')
export const Map = memo(props => {
const paperTheme = useTheme();
var mapTheme = 'standard';
if (paperTheme.dark === true) {
mapTheme = 'hybrid';
}
var backGroundColor = '#404040';
return (
<MapView
provider="google"
style={styles.map}
mapType='standard'
showsUserLocation={true}
followUserLocation={true}
region={props.getRegion}
tintColor='#404040'
overlays={[{
coordinates: props.routeCoordinates,
strokeColor: '#F02A4B',
lineWidth: 10,
}]}
>
<Polyline
coordinates={props.routeCoordinates}
strokeColor='#F02A4B'
strokeWidth={8}
/>
</MapView>
);
});
The main problem is probably that in useEffect
, you start watching the position. The second argument you use is [state]
. Inside of useEffect
, you also update the state
. This means that whenever [state]
changes, your useEffect
will run again! This means infinite loop you might end up with hundreds of listeners to the location in just a few minutes. To fix this, you probably want to pass an empty array as the second argument. Read more about it in the React hooks docs about the second parameter
Another minor error: you set this.watchId
but use watchId
in the cleanup function. You probably want to change the line with this.watchid
to:
this.watchID = navigator.geolocation.watchPosition((position) => {
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.