首页 > 解决方案 > 使用 React Typescript 在 react-leaflet-markerclusterer 中显示所有集群标记

问题描述

react-leaflet我的 React Typescript 应用程序使用& 显示带有标记集群的传单地图react-leaflet-markerclusterer

但是,我无法让地图显示地图视图内的所有集群标记。我正在尝试将此 JS 解决方案转换为 Typescript。

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

不能在回调中调用 React Hook “useMap”。必须在 React 函数组件或自定义 React Hook 函数中调用 React Hooks

在 VSCode 中,还有一个 Typescript 错误

对象可能是“未定义”.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>
    )
}

标签: javascriptreactjstypescriptleafletreact-leaflet

解决方案


这不是打字稿问题,这是反应问题。钩子需要在函数体的顶层调用,或者作为另一个自定义钩子的一部分。这同样适用于 react-leaflet 的useMap。你可以把它移到外面useEffect。但是,useMap钩子调用必须是作为子组件的一部分,MapContainer以便它可以访问传单上下文对象。您可以将此逻辑抽象到它自己的组件中,就像在这个示例中一样,但我认为您最好使用whenCreated带有状态变量的 a 来获取 的实例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 应该足够聪明,可以知道这一点groupRef并被map定义,因为它们在if声明中,但它可能仍然会抱怨group被定义。您也可以if (groupRef && groupRef.current && map)消除所有疑虑。

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


推荐阅读