在 useEffect 之外使用传单地图对象进行反应

Use leaflet map object outside useEffect in react

我有一个按钮可以编辑 mymap 对象(例如添加标记或圆圈)。 但它给我的错误是“mymap is not defined”。 我曾尝试在 useEffect 之外声明 mymap var,但它不起作用,因为 useEffect 不保存分配。我也试过 useState 但它也不起作用。

这是我的代码

import 'leaflet/dist/leaflet.css'
import L from 'leaflet/dist/leaflet'
import { useEffect, useState} from 'react'

import './css/grid.css'

export const Grid = ({mapID}) => {
    const centerCoor = //redacted
    var layerGroup = L.layerGroup()

    function addGrid(){
        var btnel = document.getElementById("btn-grid");
        btnel.classList.toggle("btn-active");

        if (btnel.classList.contains("btn-active")){
            // do something with "mymap"
            mymap.on('click', (e)=>{
            })
        }
    }

    useEffect(()=>{
        var mymap = L.map(mapID, {
            center: centerCoor,
            zoom:18
        });

        L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
            attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
            maxZoom: 18,
            id: 'mapbox/satellite-v9',
            tileSize: 512,
            zoomOffset: -1,
            accessToken: //redacted
            }
        ).addTo(mymap);


        

        return () => {
            mymap.off()
            mymap.remove()
        }
    },[])


    return (
        <div>
            <button onClick={addGrid} className="" id="btn-grid">ADD GRID</button>
            <div id={mapID}>
            </div>
        </div>
    )
}

Leaflet 独立于 React 跟踪其状态,这就是为什么最好使用 React-Leaflet binding。如果你打算在 React 中使用 vanilla Leaflet,你应该考虑设置一个 useEffect 钩子来处理创建地图实例并将地图实例设置为状态。然后你可以访问地图实例来添加事件监听器,添加 markers/popups 等。这也有助于 React 跟踪 Leaflet 的当前状态并监视任何更改。这是一个示例组件:

import React, { useEffect, useRef, useState } from 'react';
import L from 'leaflet';

const MapComponent = (props) => {
  // Map state:
  const [mapInstance, setMapInstance] = useState(null);
  const [marker, setMarker] = useState(null);

  // Map refs:
  const mapRef = useRef(null);
  const tileRef = useRef(null);
  const markerRef = useRef(null);

  // Base tile for the map:
  tileRef.current = L.tileLayer(
    `https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png`,
    {
      attribution:
        '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
    }
  );

  const mapStyles = {
    overflow: 'hidden',
    width: '100%',
    height: '100vh',
  };

  // Options for our map instance:
  const mapParams = {
    center: [37.0902, -95.7129], // USA
    zoom: 3,
    zoomControl: false,
    zoomSnap: 0.75,
    layers: [tileRef.current], // Start with just the base layer
  };

  // Map creation:
  useEffect(() => {
    mapRef.current = L.map('map', mapParams);
    // Add an event listener:
    mapRef.current.on('click', () => {
      alert('map clicked');
    });
    // Set map instance to state:
    setMapInstance(mapRef.current);
  }, []); // <- Empty dependency array, so it only runs once on the first render.

  // If you want to use the mapInstance in a useEffect hook,
  // you first have to make sure the map exists. Then, you can add your logic.
  useEffect(() => {
    // Check for the map instance before adding something (ie: another event listener).
    // If no map, return:
    if (!mapInstance) return;
    if (mapInstance) {
      mapInstance.on('zoomstart', () => {
        console.log('Zooming!!!');
      });
    }
  }, [mapInstance]);

  // Toggle marker on button click:
  const handleClick = () => {
    if (marker) {
      marker.removeFrom(mapInstance);
      markerRef.current = null;
    } else {
      markerRef.current = L.marker([40.7128, -74.006]).addTo(mapInstance);
    }
    setMarker(markerRef.current);
  };

  return (
    <>
      <button onClick={handleClick}>
        {`Click to ${marker ? 'remove' : 'add'} marker`}
      </button>
      <div id="map" style={mapStyles} />
    </>
  );
};

export default MapComponent;

这是我设置的实时沙箱,您可以对其进行测试: LIVE DEMO