Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GeoJSON data not rendered after change #332

Closed
7 tasks done
resetko opened this issue May 25, 2017 · 18 comments
Closed
7 tasks done

GeoJSON data not rendered after change #332

resetko opened this issue May 25, 2017 · 18 comments

Comments

@resetko
Copy link

resetko commented May 25, 2017

Please make sure to check the following boxes before submitting an issue. Thanks!

  • All peer dependencies are installed: React, ReactDOM and Leaflet.
  • Using a supported version of React and ReactDOM (v15.0.0+).
  • Using the supported version of Leaflet (v1.0.0) and its corresponding CSS file is loaded.
  • Using the latest version of React-Leaflet.
  • The issue has not already been reported.
  • Make sure you have followed the quick start guide for Leaflet.
  • Make sure you have fully read the documentation and that you understand the technical considerations.

Expected behavior

GeoJSON data should be re-rendered on the map after data prop change

Actual behavior

GeoJSON data rendered only once.

Steps to reproduce

Create custom component with Map and GeoJSON components. Pass prop from custom component to GeoJSON component. Change prop. See that old data still rendered.
ex: https://stackoverflow.com/questions/44155385/rendering-geojson-with-react-leaflet

@PaulLeCam
Copy link
Owner

This is the expected behavior, as documented.

@resetko
Copy link
Author

resetko commented May 26, 2017

@PaulLeCam Can you suggest how to achieve desired behavior?

@PaulLeCam
Copy link
Owner

You can set an unique key on the <GeoJSON> element every time you want it to be changed. In practice this will cause the existing layer to be removed from Leaflet and a new GeoJSON layer to be created with the provided data.

@rsancheza09
Copy link

rsancheza09 commented Oct 6, 2017

@PaulLeCam do you have a simple example of how can we implement your suggestion?

Thanks

@PaulLeCam
Copy link
Owner

There is nothing specific about it, it's just React behavior.
Put a different key on an element and it will be updated, in this case <GeoJSON key="whatever" data={geojson} /> for example.

@rsancheza09
Copy link

Thanks man. I did something like that. But in my case it is not working. I sent you by twitter a piece of code.

Regards

@PaulLeCam
Copy link
Owner

I don't do support by Twitter, please use StackOverflow if you have questions regarding using the library.

@tugrul
Copy link

tugrul commented Apr 5, 2019

This working behavior is useless. Changing key is just workaround.

I think <GeoJSON /> should act like React.PureComponent.

@tugrul
Copy link

tugrul commented Apr 5, 2019

Yet another workaround.

import React, { Component } from 'react';
import {GeoJSON} from 'react-leaflet';
import axios from 'axios';

import SymfonyRouterContext from './SymfonyRouterContext';

class ServiceGeoJson extends Component {

    constructor(props, context) {
        super(props, context);

        this.state = {
            data: []
        };

        this.geoJsonLayer = React.createRef();
    }

    componentDidMount() {

        const {service, params, zoom, center} = this.props;

        axios.post(this.context.generate(service, params), {zoom, center}).then(({data}) => {
            this.geoJsonLayer.current.leafletElement.clearLayers().addData(data);
            this.setState({data});
        });

    }

    render() {
        return <GeoJSON data={this.state.data} ref={this.geoJsonLayer}/>;
    }

}

ServiceGeoJson.contextType = SymfonyRouterContext;

export default ServiceGeoJson;

@sbland
Copy link

sbland commented Nov 20, 2020

Use ref worked out much smoother than using the component key and stopped the flashing on each update.

I implemented this with the useEffect and useRef hooks below (Note we do not need leafletElement):

const geoJsonLayer = useRef(null);

useEffect(() => {
    if (geoJsonLayer.current) {
      geoJsonLayer.current.clearLayers().addData(geoData);
    }
}, [geoData]);
...
<GeoJSON
    ref={geodataRef}
    // key={`${datakey}-main`}
     data={geoData}
    onEachFeature={onEachGeoFeature}
    style={featureStyling}
/>

@guptaji048
Copy link

Use ref worked out much smoother than using the component key and stopped the flashing on each update.

I implemented this with the useEffect and useRef hooks below (Note we do not need leafletElement):

const geoJsonLayer = useRef(null);

useEffect(() => {
    if (geoJsonLayer.current) {
      geoJsonLayer.current.clearLayers().addData(geoData);
    }
}, [geoData]);
...
<GeoJSON
    ref={geodataRef}
    // key={`${datakey}-main`}
     data={geoData}
    onEachFeature={onEachGeoFeature}
    style={featureStyling}
/>

Somehow this is not possible in version 3 or above.

@jnachtigall
Copy link

@guptaji048 Maybe because there's a typo in your quoted code: variable is called geoJsonLayer but is referenced as ref={geodataRef}. FWIW, the workaround works for me with the current react-leaflet version 3.

Here is my TypeScript component. The only difference is that style settingsare also handled which seem to be removed by the clearLayers():

import React, { ReactElement, useEffect, useRef } from "react";
import { GeoJSON, GeoJSONProps } from "react-leaflet";
import { GeoJSON as LeafletGeoJSON } from "leaflet";

/**
 * GeoJsonWithUpdates is a wrapper around react-leaflet's GeoJSON component to support data changes
 * See https://github.com/PaulLeCam/react-leaflet/issues/332
 *
 * It accepts the same props like react-leaflet's GeoJSON component.
 * However, updates are only support
 */
export default function GeoJsonWithUpdates(props: GeoJSONProps): ReactElement {
  const geoJsonLayerRef = useRef<LeafletGeoJSON | null>(null);

  useEffect(() => {
    const layer = geoJsonLayerRef.current;
    if (layer) {
      layer.clearLayers().addData(props.data);
      // clearLayers() seems to remove the `pathOptions`, `style` and `interactive` prop as well
      // Resetting it here
      if (props.pathOptions) {
        layer.setStyle(props.pathOptions);
      } else if (props.style) {
        layer.setStyle(props.style);
      }
    }
  }, [props.data, props.pathOptions, props.style]);

  return <GeoJSON {...props} ref={geoJsonLayerRef} />;
}

FWIW, for anyone using onEachFeature, you will also need to use the workaround described in #697 (comment)

@frafra
Copy link

frafra commented Oct 31, 2021

The workaround does not seem to work when pointToLayer is involved.

It would be nice to have a PR and have this issue opened again, as it is a bug indeed. Changing key is a workaround.

@worc
Copy link

worc commented Jan 21, 2022

definitely a design bug. it completely breaks with your day-to-day functional React expectations. if the props change, you expect a re-render. as it is now, there's a decent amount of React and Leaflet plumbing you have to know and exploit to fix the lifecycle issues here.

@Waffleboy
Copy link

LeafletGeoJSON

hey @jnachtigall , thanks so much for this!

I'm using this as a component and calling it in app,js as below, but i think the ref gets overwritten by the internal ref in your return, do you have any advice on how to fix this?

<GeoJsonWithUpdates
              data={data}
              onEachFeature={onEachFeature}
              style={(item) => geoJSONCellStyler(item, selectedLayer.value)}
              ref={ref}
            ></GeoJsonWithUpdates>

@frafra
Copy link

frafra commented Feb 17, 2022

@PaulLeCam Are you willing to reopen this issue?

@synmaxKP
Copy link

synmaxKP commented Nov 17, 2022

I have assigned the data (an object) that will change and necessitate a re-render as the key, which should technically work, but it does not. Instead I use an iterator when I encounter this now.
const [geoJsonKey, addToGeoJsonKey] = useState(1)
addToGeoJsonKey(geoJsonKey + 1)

Use the hook to increment the number every time you get new data. Seems dumb, but it works.

@jchai01
Copy link

jchai01 commented May 3, 2024

I had to change from this:

  // const geoJsonLayerRef = useRef<LeafletGeoJSON | null>(null);

to this, to get it to work:

 const geoJsonLayerRef = useRef();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests