mapcomponents/react-map-components-maplibre

Question: setData() implementation with useEffect()

MartinSteefStefko opened this issue · 5 comments

Hi all,

I am trying to solve the setData() updates of the map in useEffect(), something that you seem to have solved before in MlGeoJsonLayer.js and in other places. I am trying to fetch the data on an event, transform it, and set it with setData(). This is a very brief pattern that I am using.

import React, { useRef, useEffect, useState } from 'react';
import mapboxgl from 'mapbox-gl';
import { geoTemplateTransform } from '../helpers/geo';

const MapLibre = (props) => {
  const mapContainerRef = useRef(null);
  const [mapObject, setMapObject] = useState(null);
  const [parcelData, setParcelData] = useState({});

  useEffect(() => {
    maplibregl.accessToken = 'token';
    const map = new mapboxgl.Map({
      container: mapContainerRef.current,
      style: 'mapbox://styles/mapbox/streets-v11',
      center: [14.41854, 50.07365],
      zoom: 12,
    });

    map.on('load', () => {
      map.addSource('parcels', {
        type: 'geojson',
        data: {},
      });

      map.addLayer({
        id: 'parcel-fills-hover',
        type: 'fill',
        source: 'parcels',
        layout: {},
        paint: {
          'fill-color': '#0070FF',
          'fill-opacity': [
            'case',
            ['boolean', ['feature-state', 'hover'], false],
            0.5,
            0,
          ],
        },
      });
    });

    map.on('zoomend', () => {
      setParcelData('FETCHED DATA');
    });

    setMapObject(map);

    return () => map.remove();
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    const test = geoTemplateTransform(parcelData);
    if (mapObject) {
      // data is setted with setData(), but map is not updated. It works on mapbox-gl v2.6.0 though
      const geojsonSource =
        mapObject.getSource && mapObject.getSource('parcels');
      geojsonSource && geojsonSource.setData && geojsonSource.setData(test);
    }
  }, [parcelData]);

  return (
    <>
      <MapContainer ref={mapContainerRef} />
    </>
  );
};

export default MapLibre;

There are also other functions in code, which are using map or mapObject like map.flyTo(), or mapObject.setFeatureState(). All of them are working. I was not able to setData directly on map.getSource('parcels') and on mapObject.getSource('parcels') either. It was tested with fetched data and also with mocks. Adding the mapObject to dependency array does not work either. Do you have some ideas why this is not working or how to make it work? I am aware this is more likely a discussion and If I can reach you somewhere else we could definitely delete this thread and continue somewhere else.

Thanks!

Hi @MartinStefko
in MapComponents the map engine instances are referenced and accessed using the useRef hook.

It looks like you are using neither MapLibre gl nor MapComponents (MapLibre is just the name of that react component). My recommendation is to use a MapLibre gl/MapBox gl compatible react binding, like MapComponents (@mapcomponents/react-maplibre) or react-map-gl, that handles the hard to get right timing problems for you.

Thanks for response @cioddi! Maybe we can continue with this conversation just on maplibre/maplibre-gl-js#725 . Or maybe continue just here?

@MartinStefko sorry for not getting back to you sooner (too busy with other projects currently). I will take a look at the examples you provided this afternoon and try to reproduce the error. I will also provide you with a @mapcomponents/react-maplibre example doing the things you are trying to achieve.

Hi @cioddi , thanks, that would be great!

For everybody who stumbles upon this: The codepens and a working MapComponents example are linked in the other issue maplibre/maplibre-gl-js#725