You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React项目中react-leaflet的CircleMarker点击按钮不渲染问题求助

Hey there! Let's figure out why your CircleMarkers aren't showing up, and fix the toggle functionality while we're at it. The main issue here is that you're trying to return React components (<CircleMarker>) inside an event handler—React doesn't render JSX that's returned from event callbacks like that. Instead, we need to work directly with the Leaflet map instance you've created, and manage the markers with state so we can add/remove them properly.

Key Issues in Your Current Code

  • JSX in Event Handlers Doesn't Render: When you return <CircleMarker> inside handleOnclik, React has no way to mount those components into the DOM. You need to use Leaflet's native API to add markers directly to the map instance.
  • No State for Marker Management: You don't have a way to track which markers are currently on the map, making it impossible to implement the toggle/remove functionality.

Fixed Code with Toggle Functionality

Here's the updated Map1 component with working marker rendering and toggle behavior:

import React, { useRef, useEffect, useState } from 'react';
// Don't forget to import Leaflet CSS in your root file!
// import 'leaflet/dist/leaflet.css';

function Map1() {
  const apartments = [
    { roomType: 'shared room', geometry: [41.402610, 2.204270] },
    { roomType: 'shared room', geometry: [41.411300, 2.217630] },
    { roomType: 'private room', geometry: [41.410220, 2.212520] },
    { roomType: 'apartament sencer', geometry: [41.410630, 2.216970] },
    { roomType: 'private room', geometry: [41.409190, 2.209030] },
  ];
  const map = useRef(null);
  const [activeMarkers, setActiveMarkers] = useState([]); // Track added markers

  useEffect(() => {
    const url = 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}';
    const accessToken = 'pk.eyJ1Ijoiam9zZXQyMSIsImEiOiJjazF1bGZlcHowYjVlM2RwYW9ia2pwaWtlIn0.9n-6tKArfdSfd15Do6YxLA';
    
    // Initialize map
    map.current = L.map("map");
    const defaultCenter = [41.383, 2.173];
    const defaultZoom = 13.10;
    
    const layer = L.tileLayer(url, {
      maxZoom: 18,
      id: 'mapbox.streets',
      accessToken: accessToken,
    });
    
    map.current.setView(defaultCenter, defaultZoom);
    layer.addTo(map.current);

    // Cleanup: Remove markers and destroy map on unmount
    return () => {
      activeMarkers.forEach(marker => marker.remove());
      map.current.remove();
    };
  }, []); // Empty dependency array: run once on mount

  const handleMarkerToggle = (e) => {
    e.preventDefault();
    const markerType = e.target.name;

    // Toggle logic: remove existing markers if any
    if (activeMarkers.length > 0) {
      activeMarkers.forEach(marker => marker.remove());
      setActiveMarkers([]);
      return;
    }

    // Create new markers based on button type
    let newMarkers = [];
    switch(markerType) {
      case 'pink':
        // Show private + shared rooms
        newMarkers = apartments
          .filter(item => item.roomType === 'private room' || item.roomType === 'shared room')
          .map(item => {
            const marker = L.circleMarker(item.geometry, {
              color: '#000080',
              weight: 0.5,
              fillColor: 'blue',
              fillOpacity: 0.5
            }).addTo(map.current);
            // Optional: Add popup with room type
            marker.bindPopup(`<p>${item.roomType}</p>`);
            return marker;
          });
        break;
      case 'green':
        // Show full apartments
        newMarkers = apartments
          .filter(item => item.roomType === 'apartament sencer')
          .map(item => {
            const marker = L.circleMarker(item.geometry, {
              color: 'black',
              weight: 0.5,
              fillColor: 'green',
              fillOpacity: 0.5
            }).addTo(map.current);
            marker.bindPopup(`<p>${item.roomType}</p>`);
            return marker;
          });
        break;
      default:
        break;
    }

    setActiveMarkers(newMarkers);
  };

  // Fullscreen toggle with map size fix
  const onChangeFullScreen = () => {
    const mapEl = document.getElementById('map');
    mapEl.classList.toggle('normalscreen');
    mapEl.classList.toggle('fullscreen');
    map.current.invalidateSize(); // Tell Leaflet to update map dimensions
  };

  return (
    <>
      <div className="container">
        <div>
          <div id="map" className="normalscreen"></div>
        </div>
        <div id="basemaps-wrapper" className="leaflet-bar">
          <select id="basemaps" onChange={(e) => {
            // Add basemap switching logic
            const newLayer = L.tileLayer(url, {
              maxZoom: 18,
              id: e.target.value,
              accessToken: 'pk.eyJ1Ijoiam9zZXQyMSIsImEiOiJjazF1bGZlcHowYjVlM2RwYW9ia2pwaWtlIn0.9n-6tKArfdSfd15Do6YxLA',
            });
            map.current.eachLayer(layer => {
              if (layer instanceof L.TileLayer) map.current.removeLayer(layer);
            });
            newLayer.addTo(map.current);
          }}>
            <option>Visualització</option>
            <option value="mapbox.streets">Streets</option>
            <option value="mapbox.satellite">Satellite</option>
            <option value="mapbox.outdoors">Outdoors</option>
            <option value="mapbox.dark">Dark</option>
            <option value="mapbox.light">Light</option>
            <option value="mapbox.DarkGray">Dark Gray</option>
          </select>
          <button onClick={onChangeFullScreen}>Full Screen</button>
          <div>
            <img onClick={handleMarkerToggle} name="pink" src="images/pink" alt="habitacio" />
            <img onClick={handleMarkerToggle} name="green" src="images/green" alt="apartament" />
          </div>
        </div>
      </div>
    </>
  );
}

export default Map1;

Key Changes Explained

  1. State for Marker Tracking: The activeMarkers state array keeps track of all markers on the map, making it easy to remove them when the button is clicked again.
  2. Direct Leaflet API Usage: We use Leaflet's native L.circleMarker instead of React components to add markers directly to the map instance—this is why they now render correctly.
  3. Toggle Logic: Clicking a button first checks for existing markers: if present, it removes them and clears the state; if not, it creates and adds the relevant markers.
  4. Map Cleanup: The useEffect cleanup function removes all markers and destroys the map when the component unmounts, preventing memory leaks.
  5. Fullscreen Fix: Added map.current.invalidateSize() to the fullscreen toggle—this fixes map rendering glitches after resizing the container.
  6. Basemap Switching: Added basic logic to switch basemaps when the select menu changes (optional but handy!).

Important Notes

  • Ensure you've imported Leaflet's CSS in your root file (like index.js) to avoid styling issues:
    import 'leaflet/dist/leaflet.css';
    
  • Verify your image paths (images/pink, images/green) are correct relative to your project's public folder.

内容的提问来源于stack exchange,提问作者ccnat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:36:40