Reputation: 1817
What is the correct way to remove and add a source/layers with Mapbox? I am using React, and have issues and get an error when a the source data
prop is updated. As far as I have read in the documentation of Mapbox removeSource
should remove it before adding it again, but it is not working on a component update.
Error: There is already a source with this ID
componentDidMount() {
const { data } = this.props;
this.map = new mapboxgl.Map(config);
}
componentWillUnmount() {
const map = this.map;
map.remove();
map.removeControl(Draw, 'top-left');
}
shouldComponentUpdate(props, nextProps) {
const { data } = props;
if (JSON.stringify(data) !== JSON.stringify(nextProps.data)) {
return true;
}
return false;
}
componentDidUpdate(prevProps) {
const { data } = this.props;
if (JSON.stringify(data) !== JSON.stringify(prevProps.data)) {
this.fetchMap();
}
}
fetchMap() {
const map = this.map;
const { data } = this.props;
map.addControl(Draw, 'top-left');
map.on("load", (e) => {
if (data.features !== null) {
if (map.getSource("locations")) {
map.removeSource("locations");
}
map.addSource("locations", {
type: "geojson",
data: data
});
}
})
}
Upvotes: 4
Views: 3182
Reputation: 1031
The problem is that you call map.addSource
multiple times during your component lifecycle.
If I understood correctly, what are you trying to achieve, then you should move your map load
event handler to the componentDidMount
method:
componentDidMount() {
const { data } = this.props;
const map = new mapboxgl.Map(config);
map.addControl(Draw, 'top-left');
map.once("load", (e) => {
map.addSource("locations", {
type: "geojson",
data: data
});
this.setState({ mapIsLoaded: true });
});
this.map = map;
}
Note the usage of map.once
instead of map.on
and setting mapIsLoaded
state variable, after the map is done the loading.
Now you can handle your source update on componentDidUpdate
after map is loaded:
componentDidUpdate(prevProps) {
const { data } = this.props;
const { mapIsLoaded } = this.state;
if (!mapIsLoaded) {
return;
}
if (data !== prevProps.data) {
this.map.getSource("locations").setData(data);
}
}
That's it.
By the way, I'm the author of React Component Library for Mapbox GL JS. The project is intended to be as close as possible to the Mapbox GL JS API.
For example, this is how you can rewrite your code with the library:
<MapGL mapStyle='mapbox://styles/mapbox/light-v9'>
<Source id='locations' type='geojson' data={data} />
<Layer
id='locations'
type='circle'
source='locations'
paint={{
'circle-radius': 6,
'circle-color': '#1978c8'
}}
/>
<Draw />
</MapGL>
Upvotes: 6