You need something like:
...
const mapRef = useRef()
...
<MapGL
ref={mapRef}
onClick={e => {
const features = mapRef.current.queryRenderedFeatures(e.geometry.coordinates, { layers: ['ramps'] })
console.log(features)
}}
...
/>
Answer from greenafrican on Stack OverflowHello everyone!
React newbie here. I'm seeking your help because I am currently struggling with a feature. I managed to display a list of markers from an external file (list of 3 museums in London) and added popups to them. But now I'd like to filter them with 3 buttons: let's say button1 displays marker 1, etc.
I have no clue on how to perform such thing: my first thought would be to add some specific characteristic to each of them in my Data.js file but then I do not know how to retrieve this info and based my buttons on this filter in my main component, could you please tell me how to do it? Any hints?
Here is what I've done so far:
// Main document
import React, {PureComponent} from "react";
import ReactDOM from "react-dom";
import ReactMapGL, {Marker, Popup} from "react-map-gl";
import "./index.css";
import Data from "./Data.js";
const MAPBOX_TOKEN = "Mapbox_token";
class Mapp extends PureComponent {
constructor(props) {
super(props);
this.state = {
viewport: {
latitude: 51.50985,
longitude: -0.11892,
zoom: 12,
},
markers: Data,
selectedMarker: null
};
}
_renderMarkers() {
return (
this.state.markers.map(marker => (
<Marker
key={marker.id}
longitude={marker.longitude}
latitude={marker.latitude}>
<button
className="marker-btn"
onClick={event => {
event.preventDefault();
this.setState({selectedMarker: marker})
}}>
<img
className="marker-btn"
src="location-icon.svg"
alt="location Icon"
/>
</button>
</Marker>
))
)
}
_renderPopup() {
return(
this.state.selectedMarker && (
<Popup
longitude={this.state.selectedMarker.longitude}
latitude={this.state.selectedMarker.latitude}
onClose={() => {this.setState({selectedMarker: null})}}
offsetLeft={22.5}
>
<div>
<h3>Name: {this.state.selectedMarker.site_name}</h3>
<h4>Website: {this.state.selectedMarker.website}</h4>
</div>
</Popup>
)
)
}
render() {
return (
<div className="App">
<div className="Map">
<ReactMapGL
{...this.state.viewport}
width="100vw"
height="100vh"
mapStyle="mapbox://styles/mapbox/streets-v11"
onViewportChange={viewport => this.setState({viewport})}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
{this._renderMarkers()}
{this._renderPopup()}
</ReactMapGL>
</div>
</div>
);
}
}
document.body.style.margin = 0;
ReactDOM.render(
<Mapp />,
document.getElementById("root")
)The file containing the information of my markers:
//Data.js
const Data = [
{
"id": 1,
"latitude": 51.5206436238135,
"longitude": -0.12869802265875,
"site_name": "Museum of Writing",
"website": "http://www.museumofwriting.co.uk/"
},
{
"id": 2,
"latitude": 51.5252701073731,
"longitude": -0.1216462226263,
"site_name": "The Foundling Museum",
"website": "http://www.foundlingmuseum.org.uk/"
},
{
"id": 3,
"latitude": 51.5484218889309,
"longitude": -0.17724635343509,
"site_name": "Freud Museum",
"website": "https://www.freud.org.uk/"
}
]
export default DataThank you very much in advance !!
react native mapbox gl - MapboxGL.SymbolLayer filter expression to check if id in array - Stack Overflow
React-map-gl // How to filter with buttons and display markers
Configuring source, source-layer and filter propertites
Newest 'react-map-gl' Questions - Stack Overflow
Hello everyone!
React newbie here. I'm seeking your help because I am currently struggling with a feature. I managed to display a list of markers from an external file (list of 3 museums in London) and added popups to them. But now I'd like to filter them with 3 buttons: let's say button1 displays marker 1, etc.
I have no clue on how to perform such thing: my first thought would be to add some specific characteristic to each of them in my Data.js
file but then I do not know how to retrieve this info and based my buttons on this filter in my main component, could you please tell me how to do it? Any hints?
Here is what I've done so far:
// Main document
import React, {PureComponent} from "react";
import ReactDOM from "react-dom";
import ReactMapGL, {Marker, Popup} from "react-map-gl";
import "./index.css";
import Data from "./Data.js";
const MAPBOX_TOKEN = "Mapbox_token";
class Mapp extends PureComponent {
constructor(props) {
super(props);
this.state = {
viewport: {
latitude: 51.50985,
longitude: -0.11892,
zoom: 12,
},
markers: Data,
selectedMarker: null
};
}
_renderMarkers() {
return (
this.state.markers.map(marker => (
<Marker
key={marker.id}
longitude={marker.longitude}
latitude={marker.latitude}>
<button
className="marker-btn"
onClick={event => {
event.preventDefault();
this.setState({selectedMarker: marker})
}}>
<img
className="marker-btn"
src="location-icon.svg"
alt="location Icon"
/>
</button>
</Marker>
))
)
}
_renderPopup() {
return(
this.state.selectedMarker && (
<Popup
longitude={this.state.selectedMarker.longitude}
latitude={this.state.selectedMarker.latitude}
onClose={() => {this.setState({selectedMarker: null})}}
offsetLeft={22.5}
>
<div>
<h3>Name: {this.state.selectedMarker.site_name}</h3>
<h4>Website: {this.state.selectedMarker.website}</h4>
</div>
</Popup>
)
)
}
render() {
return (
<div className="App">
<div className="Map">
<ReactMapGL
{...this.state.viewport}
width="100vw"
height="100vh"
mapStyle="mapbox://styles/mapbox/streets-v11"
onViewportChange={viewport => this.setState({viewport})}
mapboxApiAccessToken={MAPBOX_TOKEN}
>
{this._renderMarkers()}
{this._renderPopup()}
</ReactMapGL>
</div>
</div>
);
}
}
document.body.style.margin = 0;
ReactDOM.render(
<Mapp />,
document.getElementById("root")
)The file containing the information about my markers:
//Data.js
const Data = [
{
"id": 1,
"latitude": 51.5206436238135,
"longitude": -0.12869802265875,
"site_name": "Museum of Writing",
"website": "http://www.museumofwriting.co.uk/"
},
{
"id": 2,
"latitude": 51.5252701073731,
"longitude": -0.1216462226263,
"site_name": "The Foundling Museum",
"website": "http://www.foundlingmuseum.org.uk/"
},
{
"id": 3,
"latitude": 51.5484218889309,
"longitude": -0.17724635343509,
"site_name": "Freud Museum",
"website": "https://www.freud.org.uk/"
}
]
export default DataThak you in advance! Cheeeeeers
» npm install react-map-gl
» npm install react-map-gl-geocoder
I know it's an old question but when you're using react-map-gl, use the source and layer components to add layers on your map. With the code something like:
<Source id="my-data" type="geojson" data={geojson}>
<Layer {...layerStyle} />
</Source>
Which keeps the layers "reactive". There's no point of using react-map-gl if you're going to use the vanilla JS logic with useRef.
From this GitHub page of react-map-gl, someone explained that <Source> is a PureComponent. After the first render, it does not update unless props shallowly change, i.e. oldValue !== newValue. When you mutate your JSON data, it is still the same object, so the changes are not detected by React.
I had the exact same issue while trying to pass a geoJSON object saved in a React state. The solution that solves the problem is to set a different key and force a re-render of the <Source> component:
import React, { useState } from 'react';
import Map, { Source } from 'react-map-gl';
const MyMap = () => {
const [geojson, setGeojson] = useState(initialGeojson);
const [key, setKey] = useState(0);
// Function to update GeoJSON
const updateGeojson = (newGeojson) => {
setGeojson(newGeojson);
setKey(key + 1); // Increment key to force re-render
};
return (
<Map {...viewport}>
<Source id="my-data" type="geojson" data={geojson} key={key}>
{/* Add your layers here */}
</Source>
</Map>
);
};
Or you could also use a package like uuid to generate random keys for each re-render.