Search code examples
reactjsleafletreact-adminreact-leaflet

Rendering issue with custom map component inside tabbed form of react-admin


I am using React-admin for a project where for some resources, I use the tabbed form to better organize the fields and inputs. I created a fairly simple custom map component based on react-leaflet, which I am using in these tabbed forms.

I am facing a weird issue where when the map is on other than the first tab, its contents do not display correctly. It appears as though the map "thinks" the viewport is much smaller than it actually is. Reloading the page (or even just opening developer tools in Chrome) forces re-render of the page and causes the map to start behaving correctly.

To better demonstrate, I created this simple Codesandbox project. It has a user resource from the RA tutorial with two tabs. Both contain an instance of the map component, but while the map on the first tab works correctly right away, the one on the second tab renders incorrectly.

I confess I am still kind of a noob at these things so I may well be doing something wrong, but I've been scratching my head for quite a few hours over this and I'd like to start eliminating possible culprits.

Any insight would be most welcome.

Thanks very much in advance for your time.


Solution

  • This issue has been discussed a lot in SO. If you search a bit you can find the reason. What you actually need to do is two things:

    1. use setInterval in combination with map's invalidateSize method when switching a tab and then clean it on component unmount
    2. use useEffect to change mapZoom and view since they are immutable.

    SO since you use react-leaflet version 2.7.x you need to take the map instance using a ref:

    const mapRef = useRef();
    
    useEffect(() => {
        if (!mapRef.current) return;
    
        const map = mapRef.current.leafletElement;
        const mapZoom = zoom || defaultZoom;
    
        let intervalInstance;
    
        if (!center && marker) {
          map.setView(marker, mapZoom);
          intervalInstance = setInterval(() => map.invalidateSize(), 100);
        } else if (!center) {
          map.setView([0.0, 0.0], mapZoom);
        }
        map.setZoom(mapZoom);
    
        return () => clearInterval(intervalInstance);
      }, []);
    
    <LeafletMap
       ref={mapRef}
       center={marker}
       zoom={defaultZoom}
       className={classes.leafletContainer}
    >
    

    Demo