I have recently started with React, and have run into a problem. I am trying to display a map with the users current location combined with some additional geometry. I can access these data and display them, but when it is time to show another page I want to unsubscribe from the location updates. To do this I need an id (according to the documentation of the navigator.geolocation). I am trying to store this id in a state variable, but for some reason I cannot set this id inside my useEffect function. The puzzling thing is that I also store the geometry data in a state variable, and I can set that just fine from inside the useEffect loop. I just cannot figure out what is the difference between the 'features' and the 'watcherId' state variables. When I run this code the waId gets set, but the watcher id always remains -1, and therefore I cannot use it to unsubscribe from the location changed event. I would really really appreciate any help.
import React, { useState, useEffect } from 'react';
import GeoJSON from 'ol/format/GeoJSON';
import Feature from 'ol/Feature';
import {Point} from 'ol/geom';
import {transform, fromLonLat} from 'ol/proj';
import MapWrapper from './Base_MapWrapper';
function Card_Map(props) {
const [ features, setFeatures ] = useState([]);
const [ watcherId, setWatcherId ] = useState(-1);
useEffect( () => {
const wktOptions = {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
}
const parsedFeatures = new GeoJSON().readFeatures(props.jsonData, wktOptions);
let waId = -1;
if (navigator.geolocation) {
console.log('location available');
waId = navigator.geolocation.watchPosition(function(position) {
let coorArr = [position.coords.longitude, position.coords.latitude];
let newCoors = transform(coorArr, 'EPSG:4326', 'EPSG:3857');
let fPoint = new Feature({
geometry: new Point(newCoors)
});
let theFeatures = [fPoint, ...parsedFeatures];
setFeatures(theFeatures);
});
console.log(waId);
setWatcherId(waId);
}
else {
setFeatures(parsedFeatures);
}
console.log(watcherId);
//this is the destructor
return () => {
if (watcherId != -1)
{
navigator.geolocation.clearWatch(watcherId);
}
}
},[]);
return (
<div>
<MapWrapper features={features} cssClass={props.cssClass} />
</div>
)
}
export default Card_Map
CodePudding user response:
Your problem is the fact that you are trying to use watcherId
which will keep a reference of the value before calling setWatcherId
.
I'm pretty sure that you have a warning saying that you should use watcherId
as a dependency, but this will trigger another re-render and a new set and so on.
You don't actually need a state here, you can just use waId
so you should end up with the following:
function Card_Map(props) {
const [ features, setFeatures ] = useState([]);
useEffect( () => {
const wktOptions = {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
}
const parsedFeatures = new GeoJSON().readFeatures(props.jsonData, wktOptions);
let waId = -1;
if (navigator.geolocation) {
console.log('location available');
waId = navigator.geolocation.watchPosition(function(position) {
let coorArr = [position.coords.longitude, position.coords.latitude];
let newCoors = transform(coorArr, 'EPSG:4326', 'EPSG:3857');
let fPoint = new Feature({
geometry: new Point(newCoors)
});
let theFeatures = [fPoint, ...parsedFeatures];
setFeatures(theFeatures);
});
}
else {
setFeatures(parsedFeatures);
}
//this is the destructor
return () => {
if (waId != -1)
{
navigator.geolocation.clearWatch(waId);
}
}
},[]);
return (
<div>
<MapWrapper features={features} cssClass={props.cssClass} />
</div>
)
}
If you do need the id
in another place, keep the state, but still use waId
in the cleanup function.