使用 React Typescript 在 react-leaflet-markerclusterer 中显示所有集群标记

Show All Cluster Markers in react-leaflet-markerclusterer using React Typescript

我的 React Typescript 应用程序使用 react-leaflet & react-leaflet-markerclusterer 显示带有标记簇的 Leaflet 地图。

但是,我无法让地图显示地图视图内的所有聚类标记。我正在尝试 convert this JS solution 到 Typescript。

中显示的错误之一。 JS 控制台是

React Hook "useMap" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function

在VSCode中,还有一个Typescript错误

Object is possibly 'undefined'.ts(2532)

上线

const groupBounds = group.getBounds();

为什么会这样,编写此 Typescript 代码的正确方法是什么?

import React, { useEffect, useRef } from 'react';
import { MapContainer, TileLayer, Marker, useMap } from 'react-leaflet';
import MarkerClusterGroup from 'react-leaflet-markercluster';

interface IProps {
    markers: {
        lat: number;
        lon: number;
    }[];
    
    mapCenter: {
        lat: number;
        lon: number;
    };
}

export function MapView({markers, mapCenter}: IProps): JSX.Element {

    const groupRef = useRef();
    
    useEffect(() => {
        if (groupRef !== undefined) {
            const group = groupRef.current;
            const map = useMap();                   // ERROR: React Hook "useMap" cannot be called inside a callback.
            const groupBounds = group.getBounds();  // ERROR: Object is possibly 'undefined'.ts(2532)
            map.fitBounds(groupBounds);
        }
    }, []);

    return (
        <MapContainer
            center={[mapCenter.lat, mapCenter.lon]}
            zoom={10}
            style={{ width:'100%', height:'100vh' }}
            className='markercluster-map'
        >
            <TileLayer 
                url={`https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=${access_token}`}
            />
            <MarkerClusterGroup ref={groupRef}>
                {
                    markers.map((marker, index) => (
                        <Marker 
                            position={[marker.lat, marker.lon]} 
                            key={index}
                        />
                    ))
                }
            </MarkerClusterGroup>
        </MapContainer>
    )
}

这不是 typescript 问题,这是 react 问题。挂钩 need to be called either within the top level of the function body, or as part of another custom hook. The same applies to react-leaflet's useMap. You can move it outside the useEffect. However a useMap hook call must be part of a component that is a child of the MapContainer so that it has access to the leaflet context object. You can abstract this logic into its own component, like in this example,但我认为你最好使用带有状态变量的 whenCreated 来获取 L.Map 的实例并在效果中使用它:

export function MapView({markers, mapCenter}: IProps): JSX.Element {

    const groupRef = useRef();
    const [map, setMap] = useState();

    useEffect(() => {
        if (groupRef && map) {
            const group = groupRef.current;
            const groupBounds = group?.getBounds();
            map.fitBounds(groupBounds);
        }
    }, [map, groupRef]);

    return (
        <MapContainer
          whenCreated={map => setMap(map)}
          moreProps={moreProps}
        >
           <Stuff />
        </MapContainer>
    )

}

如果 TS 仍然抱怨 groupmap 未定义,您可以对它们使用可选的 属性 运算符 ?.

if (groupRef && map) {
  const group = groupRef?.current;
  const groupBounds = group?.getBounds();
  map?.fitBounds(groupBounds);
}

Typescript 应该足够聪明,知道 groupRefmap 已定义,因为它们在 if 语句中,但它可能仍会抱怨 group 被定义。您也可以 if (groupRef && groupRef.current && map) 消除所有疑虑。

**注意:确保您使用的是与 react-leaflet v3 兼容的正确 version of react-leaflet-markercluster