slutske22 / react-esri-leaflet

react components for esri-leaflet
https://codesandbox.io/s/github/slutske22/react-esri-leaflet/tree/master/examples/ts
MIT License
37 stars 5 forks source link

VectorTileLayer not working with vite project #22

Open shaiRos opened 3 weeks ago

shaiRos commented 3 weeks ago

I have an app created with vite. Installed this library and I'm trying to use a vector tile to display in the map. I have tried the url used for the example for this library and even from the esri documentation here for vector tiles. Even created another repo where it's just the base template that vite installs (react-javascript)

image

and it's giving me this main error Uncaught TypeError: Cannot use 'in' operator to search for '_leaflet_id' in undefined

Things I tried:

help would be apprecitated.

slutske22 commented 3 weeks ago

Thank you for reporting this issue (and doing a well-written writeup).

I am able to reproduce this by spinning up an npm create vite reproduce-example-project with react and typescript, installing everything, and trying to use VectorTileLayer.

This is extremely odd. I have no had this issue when using parcel or webpack. Let me do some digging and see if I can come up with anything as to why using this library with vite creates this issue.

Have you tried using the "vanilla" version of a VectorTileLayer with Vite? Does that work? If that causes the same problem, it helps debug whether the issue in is this wrapper library, or upstream somewhere.

slutske22 commented 3 weeks ago

sigh so this problem is very strange indeed. Looking into it, the author of leaflet answered another person with the same here: Cannot read property '_leaflet_id' of undefined:

This happens when tilelayer variable is undefined at the moment when this line executes. Put a debugger statement there and figure it out, shouldn't be hard.

Digging into the leaflet src code, when the map.removeLayer event fires, if the layer that is being removed doesn't exist, this will happen because of this:

https://github.com/Leaflet/Leaflet/blob/eee1a4aeddd8f7bf6172ea902e32aab97770a470/src/layer/Layer.js#L179

Screenshot 2024-06-10 at 10 55 20 AM

IMO this is a small area where the leaflet src code could probably be improved. I don't personally understand why removeLayer is firing at all, or why its firing with an undefined layer in this particular use case. However, a workaround for now is to do this: in your code, anywhere after importing leaflet, but before using a VectorTileLayer, add this:

  L.Map.include({
    removeLayer(layer: L.Layer) {
      if (!layer) return this;

      const id = L.Util.stamp(layer);

      if (!this._layers[id]) {
        return this;
      }

      if (this._loaded) {
        layer.onRemove(this);
      }

      delete this._layers[id];

      if (this._loaded) {
        this.fire("layerremove", { layer });
        layer.fire("remove");
      }

      // @ts-expect-error leaflet TS very incomplete
      layer._map = layer._mapToAdd = null;

      return this;
    },
  });

This will overwrite the Map.removeLayer method to not call stamp on smething that is undefined, thereby avoiding the code that would throw an error.

This is a hacky workaround, but it does work. I am trying to brainstorm why leaflet code would ever get to running Map.removeLayer on something that is undefined, in this case only. There is something under the hood of react-leaflet with createLayerComponent that may be causing this, but I'm not sure what.

If you are familiar with these things, I'm open to suggestion on what could be going wrong or how to fix it, but for now, at least you have a monkeypatch solution.

slutske22 commented 3 weeks ago

Another thought

React 18 renders everything twice when using React.StrictMode, which is a default part of a vite project (ew). While it can actually help debug problems, in this case, I think the issue is that react is mounting and unmounting the VectorTileLayer too quickly, and the underlying leaflet layer is not being created in time.

Completely separately from the other solution I posted, removing StrictMode solves the problem for me as well. Perhaps this is not a vite issue, but rather a react-esri-leaflet issue when using strictmode in react 18.

shaiRos commented 3 weeks ago

Thanks! removing the strictmode worked (and the previous solution too). I did notice when i ran the preview of (npm run build) by vite, the app ran without errors with the VectorTile line in the code, but the layer was still not added to the map. And running on build preview removes that behavior relating to strict mode...