0

I am learning react hooks. In my code I have set the initial values for plotting map as

//map state
const [mapData, setMapData] = useState(
    { lat: 40.712776, lng: -74.0059, zoom: 5 }
);

In the useEffect() call back I am loading the map once

useEffect(() => {
    initMap();
}, []); // run once

Inside the initMap() there is map.on() method to get the updated geo locations. The initMap() is

const initMap = () => {
    mapboxgl.accessToken = 'token';
    const map = new mapboxgl.Map({
        container: mapContainer,
        style: 'mapbox://styles/mapbox/streets-v11',
        center: [mapData.lng, mapData.lat],
        zoom: mapData.zoom
    }); // load the map with init values

    // update the `mapData` as map move
    map.on('moveend', () => {
        const { lng, lat } = map.getCenter(); // logs the updated lat and lon
        setMapData({ lat: lat.toFixed(4), lng: lng.toFixed(4), zoom: map.getZoom().toFixed(2) }); // suppose to set the updated value
        console.log(mapData); // not logging updated values!
    });
}

The setMapData() is not setting the state. But the map.on is called and is logging the values.

  • How do you know that the state is not being set? `setState` is asnychronous. It is only guaranteed to be available on another hook that reacts to it. – Avin Kavish Jun 01 '19 at 11:16
  • See answer from Chris here: https://stackoverflow.com/questions/55265255/react-usestate-hook-event-handler-using-initial-state – JamesH Feb 25 '21 at 08:37

2 Answers2

2

This is also the case for useSelector. I don't believe React Hooks is being supported by Mapbox in this way. I have event handlers I add onto my map and those handlers cannot access the current values of the component's useState/useSelector. These values are always the values they were when the event handlers were added to the map. I have resorted to adding another state that triggers useEffects instead of handling it all in my event handler.

edit: It was brought to my attention that you can use redux's 'useStore' hook to grab the current redux store even in the Mapbox event handlers! It's tougher to test, however, bc you have to mock the 'useStore' manually. Hope that helps!

Dae
  • 21
  • 3
  • I came across this comment by mistake and my issue was not even related to the question, but your answer made me realize that I can use `useSelector` instead and avoid `useStore` - something that I'll have to research further, but it was a huge help and great starting point to learn something, thanks a lot! – Yourin Oct 13 '20 at 14:41
1

setState is asynchronous, updated state may or may not be available to statements following it.

Use an effect hook that runs on a mapData update to react to changes.

useEffect(() => console.log(mapData), [mapData])
Avin Kavish
  • 8,317
  • 1
  • 21
  • 36
  • Avin is correct. By adding `mapData` to the array, react will call `useEffect()` when this value changes (and the initial mount). – Neil Jun 01 '19 at 12:35
  • This solved the issue [https://stackoverflow.com/a/53406363/11566024](https://stackoverflow.com/a/53406363/11566024) –  Jun 01 '19 at 14:53
  • What's wrong with this one? This is exactly what the spec says. – Avin Kavish Jun 01 '19 at 15:49