在ReactJS中实现Google Places搜索框的代码问题排查
React Google Places SearchBox 代码问题排查与修复
存在的问题分析
- LoadScript 包裹范围错误:输入框
<input>在LoadScript组件外部,导致Google Maps API加载完成前,初始化SearchBox时可能无法获取输入框元素,且API未加载时google全局对象不存在会直接报错。 - DOM 查询不符合React规范:使用
document.getElementById('pac-input')获取输入框,React中应优先用useRef引用DOM元素,避免直接操作DOM。 - 错误操作mapRef对象:
mapRef.current是挂载地图的DOM元素,并非Google Maps实例,调用mapRef.current.getMarkers()和mapRef.current.addMarker()会抛出错误,需自行维护标记点状态。 - 未绑定
places_changed事件:未给SearchBox添加places_changed事件监听,导致选择地点时handlePlacesChanged函数不会触发。 - 未处理空值情况:
searchBox初始状态为null,直接调用searchBox.getPlaces()可能引发空指针错误。 - useEffect依赖缺失:初始化逻辑的
useEffect未考虑Google Maps API加载状态,可能在API未加载时执行代码导致报错。
修复后的代码
import React, { useEffect, useRef, useState } from 'react'; import { LoadScript } from '@react-google-maps/api'; export default function GoogleMapSearchBox() { const mapRef = useRef(null); const inputRef = useRef(null); const [map, setMap] = useState(null); const [searchBox, setSearchBox] = useState(null); const [markers, setMarkers] = useState([]); const handlePlacesChanged = () => { if (!searchBox) return; const places = searchBox.getPlaces(); if (places.length === 0) return; const bounds = new google.maps.LatLngBounds(); // 清除旧标记 markers.forEach(marker => marker.setMap(null)); const newMarkers = []; places.forEach((place) => { if (!place.geometry || !place.geometry.location) { console.log('返回的地点不包含地理信息'); return; } const marker = new google.maps.Marker({ map, title: place.name, position: place.geometry.location, }); newMarkers.push(marker); if (place.geometry.viewport) { bounds.union(place.geometry.viewport); } else { bounds.extend(place.geometry.location); } }); setMarkers(newMarkers); map.fitBounds(bounds); }; useEffect(() => { if (!mapRef.current || !inputRef.current || !window.google) return; const mapInstance = new google.maps.Map(mapRef.current, { center: { lat: -33.8688, lng: 151.2195 }, zoom: 13, mapTypeId: 'roadmap', }); setMap(mapInstance); const searchBoxInstance = new google.maps.places.SearchBox(inputRef.current); mapInstance.controls[google.maps.ControlPosition.TOP_LEFT].push(inputRef.current); // 绑定事件监听 const boundsChangedListener = mapInstance.addListener('bounds_changed', () => { searchBoxInstance.setBounds(mapInstance.getBounds()); }); const placesChangedListener = searchBoxInstance.addListener('places_changed', handlePlacesChanged); setSearchBox(searchBoxInstance); // 清理函数 return () => { boundsChangedListener.remove(); placesChangedListener.remove(); markers.forEach(marker => marker.setMap(null)); }; }, [markers]); return ( <div style={{ height: '300px', width: '100%', position: 'relative' }}> <LoadScript googleMapsApiKey={process.env.GOOGLE_API_KEY} libraries={['places']} > <div id="map" ref={mapRef} style={{ height: '100%', width: '100%' }} /> <input className="mt-5" type="text" ref={inputRef} placeholder="搜索地点" style={{ position: 'absolute', top: '10px', left: '10px', zIndex: 1000, padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }} /> </LoadScript> </div> ); }
修复说明
- 调整LoadScript包裹范围:将输入框和地图容器都放在
LoadScript内部,确保API加载完成后再初始化相关组件。 - 使用useRef引用DOM元素:用
inputRef获取输入框,替代document.getElementById,符合React DOM操作规范。 - 维护标记点状态:用
markers状态数组管理地图标记点,添加新标记前清除旧标记,避免内存泄漏。 - 绑定并清理事件监听:给SearchBox添加
places_changed事件监听,组件卸载时移除所有监听和标记点,防止内存泄漏。 - 增加空值判断:在
handlePlacesChanged开头判断searchBox是否存在,避免空指针错误。 - 优化输入框样式:给输入框添加绝对定位,使其显示在地图上方,和官方示例布局一致。
内容的提问来源于stack exchange,提问作者Movindu G
相关产品推荐
相关产品推荐

