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

React中Google Maps标记添加与删除功能异常求助

React Google Maps 标记添加/删除功能异常修复

我开发了一个简单的React应用,点击地图可添加Google Maps标记,点击标记则应删除该标记。但目前存在bug,多数情况下添加和删除标记的功能无法按预期工作。以下是原实现代码:

import * as React from 'react';
import { useRef, useEffect, useState } from 'react';
import { Wrapper } from '@googlemaps/react-wrapper';
import './style.css';

export default function App() {
  const [markers, setMarkers] = useState([
    { lat: 51, lng: 16 },
    { lat: 51, lng: 19 },
    { lat: 51, lng: 22 },
  ]);
  const loc = {
    lat: 51,
    lng: 16,
  };
  const mapOptions = {
    center: loc,
    zoom: 4,
  };

  return (
    <div>
      <h1>Add and remove map markers</h1>
      <p>Click the map to add a marker, or click a marker to delete it.</p>
      <Wrapper apiKey="">
        <GoogleMap opt={mapOptions} markers={markers} setMarkers={setMarkers} />
      </Wrapper>
    </div>
  );
}

function GoogleMap(props) {
  const { opt, setMarkers, markers } = props;
  const [map, setMap] = useState();
  const ref = useRef();

  useEffect(() => {
    if (!map) return;

    const listeners = markers.map((marker) => {
      const gmarker = new google.maps.Marker({ position: marker, map: map });
      const listener = gmarker.addListener('click', ({ domEvent, latLng }) => {
        //const newMarkers = markers.filter((m) => m !== marker);
        //setMarkers(newMarkers);
        gmarker.setMap(null);
      });
      return listener;
    });

    return () => {
      listeners.forEach((listener) => {
        listener.remove();
      });
    };
  }, [map, markers, setMarkers]);

  useEffect(() => {
    if (!map) return;

    const clickListener = map.addListener('click', (e) => {
      const markerLocation = {
        lat: e.latLng.lat(),
        lng: e.latLng.lng(),
      };
      //new google.maps.Marker({ position: markerLocation, map: map });
      setMarkers([...markers, markerLocation]);
    });

    return () => {
      clickListener.remove();
    };
  }, [map, markers, setMarkers]);

  useEffect(() => {
    setMap(new google.maps.Map(ref.current, opt));
  }, [setMap]);

  return <div ref={ref} style={{ height: '400px', width: '100%' }} />;
}

问题分析与修复方案

核心问题

  • 重复创建标记:每次markers更新时,第一个useEffect会重新创建所有标记,导致地图上出现重复标记,旧标记仅移除监听器但未从地图清除。
  • 状态与视图不一致:删除标记时仅调用gmarker.setMap(null)移除地图上的标记,但未更新React状态中的markers数组,导致状态和实际显示脱节。
  • 状态更新闭包问题:添加标记时使用[...markers, markerLocation]更新状态,若闭包捕获的markers不是最新值,会导致状态更新异常。
  • 地图初始化依赖错误:第三个useEffect依赖setMap会触发不必要的重渲染,应该依赖地图容器和配置参数。

修复后的代码

import * as React from 'react';
import { useRef, useEffect, useState } from 'react';
import { Wrapper } from '@googlemaps/react-wrapper';
import './style.css';

export default function App() {
  const [markers, setMarkers] = useState([
    { lat: 51, lng: 16 },
    { lat: 51, lng: 19 },
    { lat: 51, lng: 22 },
  ]);
  const loc = {
    lat: 51,
    lng: 16,
  };
  const mapOptions = {
    center: loc,
    zoom: 4,
  };

  return (
    <div>
      <h1>添加和删除地图标记</h1>
      <p>点击地图添加标记,点击标记删除它。</p>
      <Wrapper apiKey="">
        <GoogleMap opt={mapOptions} markers={markers} setMarkers={setMarkers} />
      </Wrapper>
    </div>
  );
}

function GoogleMap(props) {
  const { opt, setMarkers, markers } = props;
  const mapContainerRef = useRef(null);
  const mapInstanceRef = useRef(null);
  const googleMarkersRef = useRef([]);

  // 初始化地图实例
  useEffect(() => {
    if (!mapContainerRef.current) return;
    mapInstanceRef.current = new google.maps.Map(mapContainerRef.current, opt);
  }, [opt]);

  // 同步标记状态与地图显示
  useEffect(() => {
    if (!mapInstanceRef.current) return;

    // 移除状态中已不存在的标记
    googleMarkersRef.current = googleMarkersRef.current.filter(gmarker => {
      const pos = gmarker.getPosition();
      const exists = markers.some(m => m.lat === pos.lat() && m.lng === pos.lng());
      if (!exists) gmarker.setMap(null);
      return exists;
    });

    // 添加状态中新出现的标记
    markers.forEach(marker => {
      const exists = googleMarkersRef.current.some(gmarker => {
        const pos = gmarker.getPosition();
        return pos.lat() === marker.lat && pos.lng() === marker.lng;
      });
      if (!exists) {
        const gmarker = new google.maps.Marker({ position: marker, map: mapInstanceRef.current });
        // 绑定删除事件:更新React状态
        gmarker.addListener('click', () => {
          setMarkers(prev => prev.filter(m => !(m.lat === marker.lat && m.lng === marker.lng)));
        });
        googleMarkersRef.current.push(gmarker);
      }
    });

    // 组件卸载时清除所有标记
    return () => {
      googleMarkersRef.current.forEach(gmarker => gmarker.setMap(null));
      googleMarkersRef.current = [];
    };
  }, [markers, setMarkers]);

  // 绑定地图点击添加标记事件
  useEffect(() => {
    if (!mapInstanceRef.current) return;

    const clickHandler = mapInstanceRef.current.addListener('click', e => {
      const newMarker = { lat: e.latLng.lat(), lng: e.latLng.lng() };
      // 使用函数式更新确保获取最新状态
      setMarkers(prev => [...prev, newMarker]);
    });

    return () => clickHandler.remove();
  }, []);

  return <div ref={mapContainerRef} style={{ height: '400px', width: '100%' }} />;
}

修复要点说明

  • 使用googleMarkersRef存储Google Maps原生标记实例,避免重复创建和状态不一致问题。
  • 标记同步逻辑对比当前markers状态和已创建的原生标记,只添加新标记、移除已删除的标记。
  • 删除标记时通过函数式更新setMarkers,确保基于最新状态修改数组,同时同步清除地图上的标记。
  • 添加标记时使用函数式更新,解决闭包捕获旧状态导致的更新异常。
  • 地图初始化依赖opt,避免不必要的重渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 01:15:03