Home > Blockchain >  React, setstate does not activate inside useEffect function for one variable, works for another
React, setstate does not activate inside useEffect function for one variable, works for another

Time:03-02

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.

  • Related