maplibre-gl-js
maplibre-gl-js copied to clipboard
setData() integration with useEffect() hook, accessToken
maplibre-gl-js version: 1.15.2
Question
Hi all,
We were using Leaflet in our company with Mapbox tiles. I have started to experiment with maplibre-gl. I have got 2 questions actually.
1.) The prototype I created is using React hooks. It was built on mapbox-gl 2.6.0 initially. I was able to migrate most of its functionality to MapLibre, only thing that is not working is setData() inside useEffect(). It’s also not working on mapbox-gl 1.13, so it’s not anything particularly special to MapLibre. I am trying to set data dynamically, get them on zoomend or dragend events, transform them and set them. Interesting enough, functions like flyTo()
useEffect(() => {
if (map) {
map.flyTo({
center: [latLngInput.lng, latLngInput.lat],
zoom: 16
})
}
}, [latLngInput]);
work, but setData()
useEffect(() => {
const data = geoTemplateTransform(mapVectorsInBboxResD);
if (map) {
map.getSource('parcels').setData(test);
}
}, [mapVectorsInBboxResD]);
don't.
map.getSource(‘source’).setData(data) won’t update the map. It was tested on real data and also mocks, with and without data transformation. A workaround for this is to remove layers and source and add source and layers on every new data occurrence. It's working, but it’s not a nice solution and it’s creating its own problems. Can anybody share some example of setData() working with React and React hooks? Or just to share some ideas, why is this currently not working? It would be nice to have some React integration examples as mapbox-gl have.
2.) I have seen there were some concerns about connecting with MapLibre to Mapbox apis. Currently in company, we are using multiple paid Mapbox apis like tiles. But we are not getting charged for map loads, obviously, because there was and still is also Leaflet. We have got our access token. So, as I understand it correctly, with this version
maplibregl.accessToken ='ourToken';
useEffect(() => {
const map = new mapboxgl.Map({
container: mapContainerRef.current,
style: 'mapbox://styles/mapbox/streets-v11',
center: [latLng.lng, latLng.lat],
zoom: 10
});
...
},[],)
there are no charges for map loads from the Mapbox side? Just for using styles api. I have read the discussion https://github.com/maplibre/maplibre-gl-js/discussions/290 and I am also wondering if there is anything particularly wrong with using token for some apis without using Mapbox-gl.
Thanks!
I'm not sure this should be a issue, it feels to me like a discussion. In any case, there's a react project to wrap mapbox/libre, I suggest to use it and see what issues you have there. Checkout awesome-maplibre for the links to the relevant projects. In 2.x accessToken will be removed from the code so the above example won't work. If you wish to use mapbox data you are free to do so by implementing the relevant code outside this library, as this library should not be vendor specific. You can also remain in version 1.x and not update to latest version of mapbox/libre. As a side note, I think mapbox is doing a great job and deserve the money they ask for to bring new features etc.
Thank you! I will look more into https://github.com/mapcomponents/react-map-components-maplibre.
Can you provide a codesandbox or codepen example with all the code involved? From the last code snippet shown in your code I have doubts even the "flyTo effect" can work like that. You are creating a "block scoped" map const within the useEffect callback. But theres is probably a crucial part of the code hidden in "..." (like setMap(map)
). If that is the case it is most likely a timing issue. Setter functions of useState hooks are executed asynchronous. So you can be sure that you can't safely access the corresponding value reference of that useState hook in the next line. That is why you need to at least include "map" (provided it actually is a useState variable) in the dependency array of the other useEffect hooks.
As mentioned above, a working (codesandbox or codepen) example will help with solving your issues.
Hi @cioddi, thank you for response. Here are examples:
1.) https://codesandbox.io/s/my-maplibre-map-1-tjd6u geoData can be loaded with addSource(). After clicking to center of the map (or anywhere) flyTo() happens. So far so good.
2.) https://codesandbox.io/s/my-maplibre-map-2-5mr3v geoData being set by setData() does nothing, while flyTo() works after clicking to the center of the map. Or the entire code stops executing saying "Cannot getSource() of undefined which is interesting because -
3.) https://codesandbox.io/s/my-mapbox-map-5f263 in mapbox-gl version 2.6.1 of code, both setData() and flyTo() works. Even the official mapbox-gl example is using setMap(map) pattern https://github.com/mapbox/mapbox-react-examples/blob/master/data-overlay/src/Map.js
It looks like the Problem with the MapLibre example is, that it expects a valid geojson resource to be passed to the addSource call. Once the data property value is changed to at least an empty FeatureCollection it works fine for me.
map.addSource("parcels", {
type: "geojson",
data: {
type: "FeatureCollection",
features: []
}
});
Hi @cioddi , yes you are right, after the addition everything works like a charm! This really made my day! 😃 Thank you a lot!
@MartinStefko I have also created a MapComponents example for your use case.
https://codesandbox.io/s/mapcomponents-example-setdata-g3tgi
One thing I am not 100% happy with is the duplicate use of MlGeoJsonLayer with the same geojson data. I have created an issue for that (https://github.com/mapcomponents/react-map-components-maplibre/issues/4).
Note that the MlGeoJsonLayer props geojson, paint and layout are reactive to changes in the jsx of the parent component and they will always clean up after themselfes. That way you can also just embed & display smaller geojson datasets based on the truthyness of a geojson state variable.
Thanks @cioddi, great example!
To duplicated layers, yes sure, having multiple layers inside one MlGeoJson layer would be great.
Or having layers data outside of component and map() over them could also help. Is there anything not compatible with this approach?
And to make it really 1:1, there have to be some handler attached to the counter (or other solution), that's preventing the layer from appearing at the first map load (render). We are really mostly using it with onZoomEnd and onDragEnd events
Is this an issue here? I'm not sure I understand what need to be done. Can this be closed?
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 30 days.