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
相关产品推荐
相关产品推荐

