Issue in the compoentDidMount actions? - react-native

Problem:
I am creating react native application with Google maps. This is how my code is structured.
import React, { Component } from "react";
import {
View,
Text,
StyleSheet,
Dimensions,
Button,
TouchableOpacity
} from "react-native";
import { MapView } from "expo";
import {
Ionicons,
Foundation,
Entypo,
MaterialCommunityIcons
} from "#expo/vector-icons";
const windowheight = (Dimensions.get("window").height * 80) / 100;
const windowwidth = (Dimensions.get("window").width * 80) / 100;
class Parking extends Component {
static navigationOptions = {
title: "Parking",
headerStyle: {
backgroundColor: "#06153b"
},
headerTintColor: "#fff",
headerTitleStyle: {
color: "#ffff"
}
};
state = {
focusedLocation: {
latitude: 6.9218374,
longitude: 79.8211859,
latitudeDelta: 0.0322,
longitudeDelta:
(Dimensions.get("window").width / Dimensions.get("window").height) *
0.0322
},
locationChosen: false,
placesList: []
};
componentDidMount() {
navigator.geolocation.getCurrentPosition(
pos => {
const coordsEvent = {
nativeEvent: {
coordinate: {
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
}
}
};
this.pickLocationHandler(coordsEvent);
},
err => {
console.log(err);
alert("Fetching the Position failed");
}
);
}
reloadLocation = () => {
navigator.geolocation.getCurrentPosition(
pos => {
const coordsEvent = {
nativeEvent: {
coordinate: {
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
}
}
};
this.pickLocationHandler(coordsEvent);
},
err => {
console.log(err);
alert("Fetching the Position failed");
}
);
};
pickLocationHandler = event => {
this.setState({ locationChosen: true });
const coords = event.nativeEvent.coordinate;
let placesList = [];
let places = [];
this.map.animateToRegion({
...this.state.focusedLocation,
latitude: coords.latitude,
longitude: coords.longitude
});
const apikey = "myKey";
fetch(
"https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=" +
coords.latitude +
"," +
coords.longitude +
"&radius=800" +
"&type=parking" +
"&key=" +
apikey
)
.then(response => response.json())
.then(responseJson => {
if (responseJson) {
placesList = responseJson.results;
placesList.map((el, index) => {
var place = {
title: el.name,
coordinates: {
latitude: el.geometry.location.lat,
longitude: el.geometry.location.lng
}
};
places.push(place);
});
this.setState({ placesList: places });
}
});
};
componentWillUnmount = () => {
this.setState(prevState => {
return {
focusedLocation: {
...prevState.focusedLocation,
latitude: 0,
longitude: 0
},
locationChosen: false,
placesList: []
};
});
};
render() {
let marker = null;
if (this.state.locationChosen) {
marker = <MapView.Marker coordinate={this.state.focusedLocation} />;
}
const places = this.state.placesList;
return (
<View style={styles.container}>
<MapView
initialRegion={this.state.focusedLocation}
showsUserLocation={true}
style={styles.map}
onPress={this.pickLocationHandler}
ref={ref => (this.map = ref)}
>
{places.map((place, index) => {
return (
<MapView.Marker
key={index}
coordinate={place.coordinates}
title={place.title}
pinColor="violet"
/>
);
})}
{marker}
</MapView>
</View>
);
}
}
export default Parking;
const styles = StyleSheet.create({
container: {
width: "100%",
alignItems: "center",
paddingBottom: 10,
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10
},
map: {
height: "100%",
width: "100%"
},
button: {
margin: 8
},
callout: {},
calloutButton: {
marginTop: windowheight,
marginLeft: windowwidth,
borderWidth: 1,
borderColor: "rgba(0,0,0,0.2)",
alignItems: "center",
justifyContent: "center",
width: 50,
height: 50,
backgroundColor: "#2b78fe",
borderRadius: 100,
shadowColor: "#e9ebee"
}
});
In the componentDidmount method, I am getting the users current location and render the map according to that. When I open the app and go this page which in this component It loads the map as I needed but when I go to another component and come again to this component it just loads the map according to the place in the state. Can someone help me to solve this problem and Is their way to load the map with new location automatically, when the user's location changes?Thank you!

Its because, you are using StackNavigator and the componentDidMount only get called in the mounting phase.
When you navigate to maps component first time componentDidMount is called, When you navigate to some other component from maps component(maps component doesn't get unmounted in case of stack navigator). When you navigate back it just focuses/updates the already mounted maps component and componentDidMount will not get called. Hopefully render and componentDidUpdate gets called.
If you dont want to change the logic then:
One solution to this problem is instead of navigate() use push(),
this.props.navigation.push(routeName) //react-navigation-v3
push() function will push the new route component into the satck rather than navigating to previous same component in the stack.
This differs from navigate() in that navigate will pop back to earlier in the stack if a route of the given name is already present there. push will always add on top, so a route can be present multiple times.
Second Solution You might wanna check this alternative approach
class MyComponent extends React.Component {
state = {
isFocused: false
};
componentDidMount() {
this.subs = [
this.props.navigation.addListener("didFocus", () => this.setState({ isFocused: true })),
this.props.navigation.addListener("willBlur", () => this.setState({ isFocused: false }))
];
}
componentWillUnmount() {
this.subs.forEach(sub => sub.remove());
}
render() {
// ...
}
}
Third Solution
When you navigate back, also set the state(current location) in componentDidUpdate Note. you need a condition before setting the state in componentDidUpdate, other wise you will end up triggring infinite loop.

Related

How do I add permissions for react-native-maps for showUserLocation in MapView component in expo?

I am a little knew the react native, and I am not so familiar with react native permissions with expo. This is my code for the MapView component.
import MapView, { Marker, Callout } from 'react-native-maps';
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Modal, Button } from 'react-native';
import * as Location from 'expo-location';
export default function Map(props) {
let handler = props.handler;
let show = props.show;
const [location, setLocation] = useState({coords: {longitude: 0, latitude: 0}});
const [errorMsg, setErrorMsg] = useState(null);
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
})();
});
let text = "wating...";
if (errorMsg) {
text = errorMsg;
}
return (
<Modal animationType = 'slide' transparent = {true} visible = {show}>
<View style={styles.container}>
<Button title = 'CLOSE' color = 'white' onPress = {handler}/>
<MapView
showUserLocation = {true}
style={styles.map}
initialRegion={{
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
>
<Marker coordinate = {{latitude: location.coords.latitude,
longitude: location.coords.longitude}} pinColor = 'black'>
<Callout>
<Text>Im here</Text>
</Callout>
</Marker>
</MapView>
</View>
</Modal>
);
}
const styles = StyleSheet.create({
container: {
height: '80%',
margin: 10,
marginTop: '30%',
padding: 10,
paddingTop: 0,
backgroundColor: '#2997FF',
borderRadius: 10,
justifyContent: 'space-between',
alignItems: 'center'
},
map: {
width: '100%',
height: '92%',
},
paragraph: {
margin: 50
}
});
I can't find a clear answer on how to add permissions even after checking the docs and a lot of videos. This module seems to work well with react native cli.
Location.js
export async function requestPermission() {
RNLocation.configure({
distanceFilter: 5.0
})
var permission = await RNLocation.requestPermission({
ios: 'whenInUse', // or 'always'
android: {
detail: 'coarse', // or 'fine'
rationale: {
title: "We need to access your location",
message: "We use your location to show where you are on the map",
buttonPositive: "OK",
buttonNegative: "Cancel"
}
}
});
return permission;
}
Main.js
useEffect(() => {
async function fetchLocation() {
var permission = await requestPermission();
if (permission) {
var location = await requestLocation();
const { coords } = location;
setLocation(coords)
} else {
dispatch(toastMessage({
message: 'Permission to access location was denied',
title: APPLICATION_NAME,
type: 'error',
}))
return
}
}
fetchLocation();
}, [])
here example see
Hope this will help you

React Native - Get Location latitude and longitude using react-native-get-location

I am creating an app which uses the phone's location. I would like to be able to get the latitude and longitude and using it as part of an api address.
I have been following this sample code using react-native-get-location and have been able to print the information in json formate but can't pull the latitude and longitude and use them.
react-native-get-location
Here is my code.
import GetLocation from 'react-native-get-location'
export default class App extends React.Component {
constructor (props) {
super(props);
this.state = {
isLoading: true,
latitude: null,
longitude: null,
location: null
};
}
_requestLocation = () => {
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 150000,
})
.then(location => {
this.setState ({
location,
isLoading: false,
});
})
.catch(error => {
const { code, message} = error;
if (code === 'CANCELLED') {
Alert.alert('location cancelled by user or by another request');
}
if (code === 'UNAVAILABLE') {
Alert.alert('Location service is disabled or unavailable');
}
if (code === 'TIMEOUT') {
Alert.alert('Location request timed out');
}
if (code === 'UNAUTHORIZED') {
Alert.alert('Authorization denied')
}
this.setState({
location: null,
isLoading: false,
});
});
}
componentDidMount() {
GetLocation.getCurrentPosition(async (info) => {
const location = await GetLocation(
info.coords.latitude,
info.coords.longitude
);
})
const fetch = require('node-fetch');
fetch('https://api.weatherapi.com/v1/forecast.json?&q=London', {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}).then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
this.setState({
isLoading: false,
dataSource: responseJson,
})
}).catch((error) => {
console.error(error);
});
}
render() {
const {location, isLoading} = this.state;
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{flex:1, paddingTop: 20}}>
<Text>{JSON.stringify(location, 0, 2)}</Text>
<View style={{flex:1, flexDirection: 'row', textAlign: 'center', paddingLeft: 90}}>
<Button
disabled={isLoading}
title="Get Location"
onPress={this._requestLocation}
/>
</View>
</View>
)
}
}
Use expo-location instead of react-native-get-location as it is very easy to implement.
Here is the working app: Expo Snack
Screenshot:
import React, { useEffect, useState } from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
// You can import from local files
let apiKey = 'YOUR_API_KEY';
import * as Location from 'expo-location';
export default function App() {
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
const [address, setAddress] = useState(null);
// const [getLocation, setGetLocation] = useState(false);
const getLocation = () => {
(async () => {
let { status } = await Location.requestPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
}
Location.setGoogleApiKey(apiKey);
console.log(status);
let { coords } = await Location.getCurrentPositionAsync();
setLocation(coords);
console.log(coords);
if (coords) {
let { longitude, latitude } = coords;
let regionName = await Location.reverseGeocodeAsync({
longitude,
latitude,
});
setAddress(regionName[0]);
console.log(regionName, 'nothing');
}
// console.log();
})();
};
return (
<View style={styles.container}>
<Text style={styles.big}>
{!location
? 'Waiting'
: `Lat: ${location.latitude} \nLong: ${
location.longitude
} \n${JSON.stringify(address?.['subregion'])}`}
</Text>
<TouchableOpacity onPress={getLocation}>
<View
style={{
height: 100,
backgroundColor: 'teal',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 10,
marginTop: 20,
}}>
<Text style={styles.btnText}> GET LOCATION </Text>
</View>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center',
},
big: {
fontSize: 18,
color: 'black',
fontWeight: 'bold',
},
btnText: {
fontWeight: 'bold',
fontSize: 25,
color: 'white',
},
});
react-native-geolocation-service is a good alternative too for fetching latitude & longitude values.
Example usage:
import GeoLocation from 'react-native-geolocation-service';
const getDeviceCurrentLocation = async () => {
return new Promise((resolve, reject) =>
GeoLocation.getCurrentPosition(
(position) => {
resolve(position);
},
(error) => {
reject(error);
},
{
enableHighAccuracy: true, // Whether to use high accuracy mode or not
timeout: 15000, // Request timeout
maximumAge: 10000 // How long previous location will be cached
}
)
);
};

How to resolve the Can't perform a React state update on an unmounted component.! Always show this whenever i display Map

I have two-component classes. First is TrackRoute1.js from which I'm passing bus location parameters and also user current location parameters to class Dispaly.js through the server. But after displaying I keep getting a memory leak that indicates that it cant perform react state update on an unmounted component. I tried everything. But it won't help.!
TrackRoute1.js
import React, { Component } from "react";
import {
ActivityIndicator,
FlatList,
Text,
View,
TouchableHighlight,
StyleSheet,
} from "react-native";
import AnimatedRegion from "react-native-maps";
import Display1 from "./Display1";
import { LogBox } from "react-native";
import CustomHeader from "../CustomHeader";
LogBox.ignoreLogs(["Setting a timer"]);
import * as Location from 'expo-location';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoading: true,
userLat: 0,
userLng: 0,
routeCoordinates: [],
distanceTravelled: 0,
prevLatLng: {},
coordinate: new AnimatedRegion({
userLat: 0,
userLng: 0,
latitudeDelta: 0,
longitudeDelta: 0
})
};
}
componentDidMount() {
this.interval = setInterval(() => this.getCoords(), 1000);
this._getLocationAsync();
}
async getCoords() {
fetch("https://api.thingspeak.com/channels/1323137/feeds.json?results=1")
.then((response) => response.json())
.then((json) => {
console.log(json.feeds, "API res");
this.setState({ data: json.feeds });
})
.catch((error) => console.error(error))
.finally(() => {
this.setState({ isLoading: false });
});
}
_getLocationAsync = async () => {
console.log("123")
let { status } = await Location.requestForegroundPermissionsAsync();
console.log(status)
if (status !== 'granted') {
console.log('Permission to access location was denied');
return;
}
// Old method //
let location = await Location.getCurrentPositionAsync({});
console.log(location, "my location");
this.setState({
userLat: Number(location.coords.latitude),
userLng: Number(location.coords.longitude)
})
console.log(this.state.userLng,"userLng111")
// New Method //
// this.location = await Location.watchPositionAsync(
// {
// enableHighAccuracy: true,
// distanceInterval: 1,
// timeInterval: 1000
// },
// newLocation => {
// let { coords } = newLocation;
// // console.log(coords);
// this.setState({
// userLat: Number(coords.latitude),
// userLng: Number(coords.longitude)
// })
// },
// error => console.log(error)
// );
// return this.location;
};
calcDistance = newLatLng => {
const { prevLatLng } = this.state;
return haversine(prevLatLng, newLatLng) || 0;
};
componentWillUnmount() {
clearInterval(this.interval);
navigator.geolocation.clearWatch(this.watchID);
}
render() {
const { data, isLoading } = this.state;
return (
<View style={{ flex: 1, width: "100%" }}>
<CustomHeader title="Track Route" navigation={this.props.navigation} />
{isLoading ? (
<ActivityIndicator />
) : (
<FlatList
data={data}
keyExtractor={(id, index) => index.toString()}
renderItem={({ item }) => (
<Display1
value1={item.field1}
value2={item.field2}
value3={item.field3}
value4={this.state.userLat}
value5={this.state.userLng}
/>
)}
/>
)}
</View>
);
}
}
const styles = StyleSheet.create({
appButtonText: {
fontSize: 18,
color: "#fff",
fontWeight: "bold",
alignSelf: "center",
textTransform: "uppercase",
},
});
Dispaly.js
import React, { Component } from "react";
import {
StyleSheet,
View,
Text,
TouchableOpacity,
Platform,
Dimensions,
Image,
} from "react-native";
import * as Location from 'expo-location';
import MapView, {
PROVIDER_GOOGLE,
Marker,Polyline,
AnimatedRegion,
} from "react-native-maps";
import AutoScrolling from "react-native-auto-scrolling";
import { getPreciseDistance } from 'geolib';
const { width, height } = Dimensions.get("window");
export default class Map extends Component {
// constructor(props) {
// super(props);
// this.state = {
// };
// }
constructor(props) {
super(props);
this.marker = null;
this.timeout = null;
this.state = {
isMapReady: false,
userLat: 0,
userLng: 0,
Time:0,
Dis: 0,
hasLocationPermissions: false,
locationResult: null,
coordinate: new AnimatedRegion({
latitude: parseFloat(this.props.value1),
longitude: parseFloat(this.props.value2),
latitudeDelta: 0.05,
longitudeDelta: 0.04,
}),
};
}
async componentDidMount() {
this.mounted = true;
this.timeout = setTimeout(()=>{ var pdis = getPreciseDistance(
{latitude:parseFloat(this.props.value1), longitude: parseFloat(this.props.value2)},
{latitude: parseFloat(this.props.value4), longitude: parseFloat(this.props.value5)},
);
// console.log(
// `Precise Distance\n\n${pdis} Meter\nOR\n${pdis / 1000} KM`
// );
this.setState({Dis:pdis,Time:Math.round( pdis/(this.props.value3*1000) )})} ,2000)
this.setState({
Time:this.state.Time.toFixed(2)
})
}
componentWillUnmount = () =>{
this.mounted = false;
if (this.timeout) {
clearTimeout(this.timeout)
}
}
onMapLayout = () => {
this.setState({ isMapReady: true });
};
render() {
// console.log( this.state.userLat,
// this.state.userLng,"myConsole")
return (
<View>
{this.state.Dis !== 0 ?
<View style={{backgroundColor:"rgba(52, 52, 52, 0.00006)"}}>
<Text style={{ color: "green", fontWeight: "bold", marginTop: 10, alignSelf: "center", marginBottom: 3, }}>Bus is {this.state.Dis / 1000} km Away.</Text>
<AutoScrolling style={{ backgroundColor: "rgba(52, 52, 52, 0.00006)",
width: 400,
padding: 10,
marginBottom: 10,}} endPadding={50}>
<Text style={ {
color: "red",
fontSize: 20,
fontWeight: "bold",
textAlign: "center",
margin: 5,
}}>Bus is Coming in {this.state.Time} Minutes</Text>
</AutoScrolling>
</View>
:null }
<MapView
initialRegion={{
latitude: parseFloat(this.props.value1),
longitude: parseFloat(this.props.value2),
latitudeDelta: 0.02,
longitudeDelta: 0.01,
}}
onMapReady={this.onMapLayout}
provider={PROVIDER_GOOGLE}
loadingIndicatorColor="#e21d1d"
ref={(map) => (this.map = map)}
style={{
width,height
}}
loadingEnabled={true}
>
{this.state.isMapReady && (
<MapView.Marker
key="AIzaSyB8k3Irk81q1k8pbj5tPM33KRWNdtROoOg"
identifier="marker"
coordinate={{
latitude: parseFloat(this.props.value1),
longitude: parseFloat(this.props.value2),
}}
flat={false}
title="Route 1"
description = "Driver: Muhammad Murtaza"
>
<Image source={require("D:/React Native apps/Bus-Track1-main/assets/car-marker.png")} style={{ width: 60, height: 50 }}/>
</MapView.Marker>
)}
<MapView.Marker
key="AIzaSyArnXt8Xqydc3BF9Udt8JCkFuKqgPR_HM0"
identifier="marker1"
coordinate={{
latitude: parseFloat(this.props.value4),
longitude: parseFloat(this.props.value5)
}}
title="My Location"
/>
<Polyline
coordinates={[
{ latitude: parseFloat(this.props.value4),
longitude: parseFloat(this.props.value5)
},
{ latitude: parseFloat(this.props.value1),
longitude: parseFloat(this.props.value2)
},
]}
strokeColor="red"
strokeColors={[
'#238C23',
'red'
]}
strokeWidth={5}
/>
</MapView>
</View>
);
}
}
I have two-component classes. First is TrackRoute1.js from which I'm passing bus location parameters and also user current location parameters to class Dispaly.js through the server. But after displaying I keep getting a memory leak that indicates that it cant perform react state update on an unmounted component. I tried everything. But it won't help.!

creating a data and pushing in to array and showing them in a map is not working correctly?

Problem:
I am creating react native app with Google map integration. This is how I have done It.
import React, { Component } from "react";
import { View, Text, StyleSheet, Dimensions } from "react-native";
import { MapView } from "expo";
import Marker from "./Marker";
class Parking extends Component {
static navigationOptions = {
title: "Parking",
headerStyle: {
backgroundColor: "#06153b"
},
headerTintColor: "#fff",
headerTitleStyle: {
color: "#ffff"
}
};
constructor(props) {
super(props);
this.state = {
focusedLocation: {
latitude: 0,
longitude: 0,
latitudeDelta: 0.0122,
longitudeDelta:
(Dimensions.get("window").width / Dimensions.get("window").height) *
0.0122
},
locationChosen: false,
placesList: []
};
}
componentDidMount() {
navigator.geolocation.getCurrentPosition(
pos => {
const coordsEvent = {
nativeEvent: {
coordinate: {
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
}
}
};
this.pickLocationHandler(coordsEvent);
},
err => {
console.log(err);
alert("Fetching the Position failed");
}
);
}
pickLocationHandler = event => {
const coords = event.nativeEvent.coordinate;
let placesList = [];
let places = [];
this.map.animateToRegion({
...this.state.focusedLocation,
latitude: coords.latitude,
longitude: coords.longitude
});
const apikey = "My API key";
fetch(
"https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=" +
coords.latitude +
"," +
coords.longitude +
"&radius=500" +
"&type=parking" +
"&key=" +
apikey
)
.then(response => response.json())
.then(responseJson => {
if (responseJson) {
placesList = responseJson.results;
console.log(placesList);
placesList.map((el, index) => {
const place = {
title: el.name,
coordinates: {
latitude: el.geometry.location.lat,
longitude: el.geometry.location.lng
}
};
places[index] = place;
});
}
});
if (places) {
this.setState({ placesList: places });
console.log(places);
}
this.setState({ locationChosen: true });
};
render() {
let marker = null;
if (this.state.locationChosen) {
marker = <MapView.Marker coordinate={this.state.focusedLocation} />;
}
const places = this.state.placesList;
return (
<View style={styles.container}>
<MapView
initialRegion={this.state.focusedLocation}
showsUserLocation={true}
style={styles.map}
onPress={this.pickLocationHandler}
ref={ref => (this.map = ref)}
>
{places.map((place, index) => {
<MapView.Marker
coordinate={place.coordinates}
title={place.title}
/>;
})}
{marker}
</MapView>
</View>
);
}
}
export default Parking;
const styles = StyleSheet.create({
container: {
width: "100%",
alignItems: "center",
paddingBottom: 10,
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10
// backgroundColor:"#192f6a"
},
map: {
height: "100%",
width: "100%"
},
button: {
margin: 8
}
});
But It is showing Nothing On the Map. When I console log the places Like this.
if (places) {
this.setState({ placesList: places });
console.log(places);
}
It shows an Empty array. If I console log the placesList inside the fetch it shows the results. Can Someone help me to solve this problem and To modify My code in order to show the markers for the places that I have got from the fetch result from google API in the map?. Thank You very Much!!.
I'm fairly new to all this but I would say a couple of things:
1 - You're declaring 'place' as a const const place = ... but you're also trying to update it within the map loop, so I'm guessing that won't work. Use var place = ... instead?
2 - Instead of places[index] = place, does places.push(place) work?

Not able to turn off location tracking when a React Native Maps component has been backgrounded

The map component of my app does not need to constantly track the user's location. Adding
navigator.geolocation.clearWatch(this.watchId);
and
navigator.geolocation.stopObserving();
to componentWillUnmount and to an AppState listener does not shut off the GPS when it's not needed. How can I make this work? Thanks.
A little more info: I can tell the GPS location service is still running (in the android version of this app) because the location icon remains in the statusbar after the app has been backgrounded.
export class MapOfHalifax extends React.Component {
constructor(args) {
super(args);
this.state = {
markers: this.props.markers,
latitude: null,
longitude: null,
error: null,
}
}
componentDidMount() {
this.watchId = navigator.geolocation.watchPosition(
(position) => {
this.setState({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
this.checkDistanceFromSignificantLocation(position)
},
(error) => this.setState({ error: error.message }),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000, distanceFilter: 10 },
);
AppState.addEventListener('change', (state) => {
if (state === 'active') {
console.log('state active');
}
if(state === 'background'){
navigator.geolocation.clearWatch(this.watchId);
navigator.geolocation.stopObserving();
console.log('background');
}
});
}
componentWillUnmount() {
navigator.geolocation.clearWatch(this.watchId);
navigator.geolocation.stopObserving();
}
toggleSelect(id) {
// this.state.selected.indexOf(id) > -1
// ? this.setState({ selected: this.state.selected.filter(x => x !== id) })
// : this.setState({ selected: this.state.selected.concat(id) }),
this.props.toggleMarkerState(id)
}
checkDistanceFromSignificantLocation(currentPosition) {
this.state.markers.map((marker, index) => {
const START = {
latitude: currentPosition.coords.latitude,
longitude: currentPosition.coords.longitude
}
const END = {
latitude: marker.latitude,
longitude: marker.longitude
}
if (haversine(START, END, { threshold: MAX_DISTANCE_FROM_LOCATION, unit: PREFERED_DISTANCE_UNIT })
&& (!this.props.markers[index].locationPassedBy)){
this.props.updatePassedByTime(index, moment.utc())
NotificationsAndroid.localNotification({
title: "Approaching:",
body: marker.name + "!"
});
} else if (haversine(START, END, { threshold: MAX_DISTANCE_FROM_LOCATION, unit: PREFERED_DISTANCE_UNIT })
&& (moment().diff(this.props.markers[index].locationPassedBy,'minutes') > 60)){
NotificationsAndroid.localNotification({
title: "Approaching:",
body: marker.name + "!"
});
}
});
}
render() {
return (
<View style={styles.container}>
<MapView
ref={ref => { this.map = ref; }}
showsUserLocation={true}
showsMyLocationButton={true}
style={styles.map}
initialRegion={{
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}>
{this.state.markers.map((marker, index) => {
return (<MapView.Marker
coordinate={{
latitude: parseFloat(marker.latitude),
longitude: parseFloat(marker.longitude)
}}
title={marker.name}
key={marker.id}
onPress={() => {
const marker = this.state.markers[index]
marker.mapMarkerIsSelected = !marker.mapMarkerIsSelected
this.setState({
markers: [
...this.state.markers.slice(0, index),
marker,
...this.state.markers.slice(index + 1)
]
})
this.props.toggleMarkerState(marker.id)
}}
pinColor={
marker.mapMarkerIsSelected ? '#3590ea' : '#f06f77'
}>
</MapView.Marker>)
})}
</MapView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
justifyContent: 'flex-end',
alignItems: 'center',
},
map: {
...StyleSheet.absoluteFillObject,
},
bubble: {
backgroundColor: 'rgba(255,255,255,0.7)',
paddingHorizontal: 18,
paddingVertical: 12,
borderRadius: 20,
},
button: {
marginTop: 12,
paddingHorizontal: 12,
alignItems: 'center',
marginHorizontal: 10,
},
buttonContainer: {
flexDirection: 'column',
marginVertical: 20,
backgroundColor: 'transparent',
},
customCallout: {
backgroundColor: 'white',
padding: 5
},
callout: {
width: 140,
},
});
const mapStateToProps = state => {
return {
markers: state.PeopleReducer
};
}
const actions = { updatePassedByTime, toggleMarkerState };
export default connect(mapStateToProps, actions)(MapOfHalifax);
When I made this post, I was running the above code on google's android emulator. After posting, it became apparent that my code was working like it should on the emulator but, with a delay. So, I ran the code on a physical device and found that the geolocation icon disappeared instantaneously. It was sufficient to leave the clearWatch() and possibly the stopObserving() functions in componentWillUnmount(). The AppState listener was not needed.