React-mapbox-gl: GeoJSON layer not updating when source changes

Created on 11 Oct 2017  路  4Comments  路  Source: alex3165/react-mapbox-gl

Hey guys! Forgive me if this question has been asked before, but I've looked around and I'm stumped. This is my first react app, and I have turned mapbox draw polygons into a geojson, added editable properties (eg height with a slider) so it updates the geojson, then this is set as the geojson layer data. See below:
<GeoJSONLayer id="draw_layer" data={this.state.drawnBuilding} fillExtrusionPaint={{ 'fill-extrusion-color': { 'type': 'identity', 'property': 'colour' }, 'fill-extrusion-height': { 'type': 'identity', 'property': 'height' }, 'fill-extrusion-base': { 'type': 'identity', 'property': 'base_height' } }} />
I have previously had the fillExtrusionPaint properties set with states, and they updated live, but was applied to the whole layer. However, I need to be able to control individual polygon properties eg setting different heights for different polygons. My geojson currently does this and is a valid geojson, but the style properties are not re-rendered on the map. Is there an event that can make the map re-render when the source changes? I have the current version (2.7.0) and the new update didn't help the problem for me.
Thanks!
Madeleine

Most helpful comment

I have run into this problem as well trying to replicate the mapbox-gl example here: https://www.mapbox.com/mapbox-gl-js/example/measure/

GeoJSONLayer from react-mapbox-gl does not support dynamic source data, but I have figured out a workaround. You need to manually update the source data in the map instance by using the componentWillUpdate lifecycle method. First though, you'll need to store a reference to the map instance in your state using the onStyleLoad prop.

onStyleLoad = (map, e) => {
  this.setState( {map} );
}

componentWillUpdate(nextProps, nextState) {
  const { map, drawnBuilding } = nextState;
  if (map) {
    map.getSource('draw_layer').setData(drawnBuilding);
  }
}

render() {
  return (
  <Map onStyleLoad={this.onStyleLoad}>
    <GeoJSONLayer
      id="draw_layer"
      data={this.state.drawnBuilding}
      fillExtrusionPaint={{
        'fill-extrusion-color': { type: 'identity', property: 'colour' },
        'fill-extrusion-height': { type: 'identity', property: 'height' },
        'fill-extrusion-base': {
          type: 'identity',
          property: 'base_height'
        }
      }}
    />
  </Map>
  )
}

The GeoJSONLayer class should absolutely be performing the map.getSource(id).setData(data) step in its own lifecycle method.

All 4 comments

I have run into this problem as well trying to replicate the mapbox-gl example here: https://www.mapbox.com/mapbox-gl-js/example/measure/

GeoJSONLayer from react-mapbox-gl does not support dynamic source data, but I have figured out a workaround. You need to manually update the source data in the map instance by using the componentWillUpdate lifecycle method. First though, you'll need to store a reference to the map instance in your state using the onStyleLoad prop.

onStyleLoad = (map, e) => {
  this.setState( {map} );
}

componentWillUpdate(nextProps, nextState) {
  const { map, drawnBuilding } = nextState;
  if (map) {
    map.getSource('draw_layer').setData(drawnBuilding);
  }
}

render() {
  return (
  <Map onStyleLoad={this.onStyleLoad}>
    <GeoJSONLayer
      id="draw_layer"
      data={this.state.drawnBuilding}
      fillExtrusionPaint={{
        'fill-extrusion-color': { type: 'identity', property: 'colour' },
        'fill-extrusion-height': { type: 'identity', property: 'height' },
        'fill-extrusion-base': {
          type: 'identity',
          property: 'base_height'
        }
      }}
    />
  </Map>
  )
}

The GeoJSONLayer class should absolutely be performing the map.getSource(id).setData(data) step in its own lifecycle method.

So the reason it isn't working is that GeoJSONLayer only updates the data if the object reference is different to the previous prop passed in. https://github.com/alex3165/react-mapbox-gl/blob/master/src/geojson-layer.ts#L268

On componentWillReceiveProps it checks if (props.data !== data) { and if they are the same it will not update the data. This means it will not update the data if the top level object is the same even if there have been changes to the object itself.

The easiest way to make sure the object reference is new is to do const new drawnBuilding = Object.assign({}, drawnBuilding) when updating drawnBuilding and before you set your state so that the reference passed to the GeoJSONLayer component is new.

Closing, the question was answered

Hello I have a similar problem. I am not sure if someone can help me answer mine.
I am trying to code save/load function. I can say my save and load function works properly because it displays on console that for example when I load "Canada" file from database, it shows that Canada's properties.color has been changed from default color "green" to red. But it seems like I ran into an issue of not updating. How can this be fixed?

//Map.jsx
import React from 'react';
import { MapContainer, GeoJSON,} from "react-leaflet";
const Map = ({countries}) => {

  const onEachCountry = (country, layer) => {
    console.log(country.properties.color) //This shows me that some of the country colors has been changed
    layer.options.fillColor = country.properties.color; // this should change the color of the map according to country.properties.color. But even after an update in country.properties.color, the color of the map does not change.
  }

  return (
    <div>
          <MapContainer>
              <GeoJSON style = {countryStyle} data = {countries} onEachFeature={onEachCountry}/>{/*Displays the map */}
          </MapContainer>
    </div> 
  );
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

radeno picture radeno  路  13Comments

rlueder picture rlueder  路  13Comments

z0d14c picture z0d14c  路  15Comments

pronebird picture pronebird  路  19Comments

danksky picture danksky  路  11Comments