如何在 react-leaflet 中使用 Typescript 设置对 'leafletElement' 的引用?

How to set the reference to the 'leafletElement' using Typescript inside react-leaflet?

问题是在我的 React 项目中使用 TS 时,我需要设置 useRef() 函数的类型,但我不知道它是什么。因此,在下面的代码中,我将其设置为 any,然后调用 leafletElement。出现的问题是即使在通过 whenCreated 函数 iside MapContainer.

引用地图后,元素始终未定义

我试图以一种显示所有 Marker 的方式设置 MapContainer 使其动态调整大小,因此当位置更改时它会更新它并调整 MapContainer因为它没有 ref 属性.

Example I'm trying to reproduce

const MapTry = () => {
const [nodes, setNodes] = useState<INode[]>([]);
const [devices, setDevices] = useState<IDevice[]>([]);

const [nodeDeviceTest, deviceNodeConnection] = useState([]);
var arrObj: any = [];
var arrOfBounds: any = [];
var mapRef = useRef<any>();
var groupRef = useRef<any>();


var device = new L.Icon({
    iconUrl: deviceCar,
    iconSize: [30, 30],
    iconAnchor: [16, 3],
    popupAnchor: [1, -1],
    shadowSize: [41, 41]
});

var deviceNode = new L.Icon({
    iconUrl: nodeDevice,
    iconSize: [20, 30],
    iconAnchor: [10, 0],
    popupAnchor: [1, -34],
    shadowSize: [41, 41],

})

useEffect(() => {
    async function fetchData() {
        const user = checkUserData();
        const project = checkProjectData();
        debugger;
        if (mapRef.current && mapRef) {
            let map = mapRef.current.leafletElement;
            let group = groupRef.current.leafletElement;
            map.fitBounds(group.getBounds());
        }



        const result = await getProjectNodeDeviceMap(project, user);
        console.log(result);
        if (result) {
            if (result.devicesToNodes) {
                for (var i = 0; i < result.devicesToNodes.length; i++) {
                    var objec = {
                        nodeId: "",
                        devId: "",
                        deviceLat: 0,
                        deviceLong: 0,
                        nodeLat: 0,
                        nodeLong: 0
                    }
                    for (var j = 0; j < result.devices.length; j++) {
                        if (result.devicesToNodes[i].deviceId == result.devices[j].deviceId) {
                            objec.deviceLat = result.devices[j].latitude
                            objec.deviceLong = result.devices[j].longitude
                            objec.devId = result.devices[j].deviceId
                            arrOfBounds.push([result.devices[j].latitude, result.devices[j].longitude])

                            for (var k = 0; k < result.nodes.length; k++) {
                                if (result.devicesToNodes[i].nodeId == result.nodes[k].id) {
                                    objec.nodeLat = result.nodes[k].latitude
                                    objec.nodeLong = result.nodes[k].longitude
                                    objec.nodeId = result.nodes[k].id
                                    arrOfBounds.push([result.nodes[k].latitude, result.nodes[k].longitude])


                                    arrObj.push(objec);
                                    break;
                                }
                            }
                        }
                    }
                    deviceNodeConnection(arrObj);
                }
            }

            if (result.nodes) {
                result.nodes.map((node: INode) => {
                    if (node.state == "running") {
                        node.color = "success";
                    }

                    else if (node.state == "error") {
                        node.color = "danger";
                    }

                    else if (node.state == "stopped") {
                        node.color = "warning";
                    }

                    else {
                        node.color = "info";
                    }
                })
            }

            setNodes(result.nodes)
            setDevices(result.devices)
        }
    }

    const timer = setInterval(() => {
        fetchData();
    }, 3000)

    return () => clearTimeout(timer);
}, [arrObj])

return (
    <div className="col-md-9">
        <div className="card shadow p-3 mb-5 bg-white rounded">
            <div className="text-center">
                <h3>Showcase</h3>
            </div>
            {devices && devices.length > 0 && <MapContainer
                whenCreated={mapInstance => { mapRef.current = mapInstance }}
                center={[devices[0].latitude, devices[0].longitude]}
                zoom={6}
                scrollWheelZoom={true}
                minZoom={2}
                maxBoundsViscosity={1.0}
                zoomControl={false}
                style={{ backgroundColor: "inherit", width: "100%", height: "60vh" }}
            >
                <TileLayer
                    attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                    url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                />
                {nodeDeviceTest.length > 0 && nodeDeviceTest?.map((point: any) => {
                    return <Polyline key={point.deviceId} positions={[
                        [point.nodeLat, point.nodeLong], [point.deviceLat, point.deviceLong]
                    ]} color={'blue'} dashArray="5,10" />
                })}
                <FeatureGroup ref={groupRef}>
                    {nodes?.map((point: any) => (
                        <Marker
                            zIndexOffset={100}
                            icon={deviceNode}
                            key={point.Id}
                            position={[
                                point.latitude,
                                point.longitude
                            ]}
                        >
                            <Popup>
                                <br />
                                Node: {point.name}
                                <br />
                                State:
                                <span className={`text-${point.color}`}>
                                    <b>{point.state}</b>
                                </span>
                            </Popup>
                        </Marker>
                    ))}
                    {devices?.map((dev: any) => {
                        return <Marker
                            key={dev.deviceId}
                            position={[
                                dev.latitude,
                                dev.longitude
                            ]}
                            icon={device}
                        >
                            <Popup>
                                Device id: {dev.deviceId}
                            </Popup>
                        </Marker>
                    })}
                </FeatureGroup>
            </MapContainer>}
        </div>
    </div>

)}

此外,如果我在解释中遗漏了什么,我希望 FeatureGroup 是正确的 referenced.Sorry。我是 React/TS.

的新手

编辑:这是我创建的示例代码

const Mapp = () => {
const devices = [{
    deviceId: "1-528a-4f80-9c7a-124108f86895",
    latitude: 45.5,
    longitude: 18.7
},
{
    deviceId: "2-528a-4f80-9c7a-124108f86895",
    latitude: 60.5,
    longitude: 18.7
}]

const nodes = [{
    id: "test-node-1",
    latitude: 45,
    longitude: 15.4566,
    name: "TestNode1",
    state: "running",
},
{
    id: "test-node-2",
    latitude: 47,
    longitude: 15.4566,
    name: "TestNode2",
    state: "running",
}]

const devicesToNodes = [{
    deviceId: "1-528a-4f80-9c7a-124108f86895",
    nodeId: "test-node-1"
},
{
    deviceId: "2-528a-4f80-9c7a-124108f86895",
    nodeId: "test-node-1"
}]

const arrOfLatLang = [{
    deviceLat: 45.5,
    deviceLong: 18.7,
    nodeLat: 47,
    nodeLong: 15.4566
},
{
    deviceLat: 60.5,
    deviceLong: 18.7,
    nodeLat: 47,
    nodeLong: 15.4566
}]


return (
    <MapContainer
        style={{ marginLeft: "200px" }}
        center={[devices[0].latitude, devices[0].longitude]}
        zoom={6}
        scrollWheelZoom={true}
        bounds={[[90, 180], [-90, 180]]}
        minZoom={2}
        maxBoundsViscosity={1.0}
    >
        <TileLayer
            attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />
        {arrOfLatLang.length > 0 && arrOfLatLang?.map((point: any) => {
            return <Polyline key={point.deviceId} positions={[
                [point.nodeLat, point.nodeLong], [point.deviceLat, point.deviceLong]
            ]} color={'blue'} dashArray="5,10" />
        })}

        {nodes?.map((point: any) => (
            <Marker
                zIndexOffset={100}
                key={point.Id}
                position={[
                    point.latitude,
                    point.longitude
                ]}
            >
                <Popup>
                    <br />
                    Node: {point.name}
                    <br />
                    State:
                    <span className={`text-${point.color}`}>
                        <b>{point.state}</b>
                    </span>
                </Popup>
            </Marker>
        ))}
        {devices?.map((dev: any) => {
            return <Marker
                key={dev.deviceId}
                position={[
                    dev.latitude,
                    dev.longitude
                ]}
            >
                <Popup>
                    Device id: {dev.deviceId}
                </Popup>
            </Marker>
        })}
    </MapContainer>
)}

您使用了底层传单实例类型。对于地图,其 L.Map,对于要素组,其 L.FeatureGroup:

var mapRef = useRef<L.Map>();
var groupRef = useRef<L.FeatureGroup>();

请注意,在 whenCreated 中设置地图参考,就像您在 whenCreated={mapInstance => { mapRef.current = mapInstance }} 中所做的那样,这不是一个好主意,并且可能会导致您尝试输入的方式出现问题参考。我建议在 MapContainer (ref={mapRef}) 上使用适当的 ref 属性,或者将 mapRef 设置为状态变量,如 example in the react-leaflet docs.

中所述