React集成Google Maps API报错:组件类型无效问题求助
问题描述
用React开发网页,通过Google Maps API展示实时地图,给一组地理地址数组添加标记时,出现以下错误:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
错误栈:
Check the render method of `Map`. at createFiberFromTypeAndProps (http://localhost:3001/static/js/bundle.js:47804:21) at createFiberFromElement (http://localhost:3001/static/js/bundle.js:47825:19) at reconcileSingleElement (http://localhost:3001/static/js/bundle.js:36916:27) at reconcileChildFibers (http://localhost:3001/static/js/bundle.js:36966:39) at reconcileChildren (http://localhost:3001/static/js/bundle.js:39908:32) at mountIndeterminateComponent (http://localhost:3001/static/js/bundle.js:40745:9) at beginWork (http://localhost:3001/static/js/bundle.js:41973:20) at HTMLUnknownElement.callCallback (http://localhost:3001/static/js/bundle.js:26983:18) at Object.invokeGuardedCallbackDev (http://localhost:3001/static/js/bundle.js:27027:20) at invokeGuardedCallback (http://localhost:3001/static/js/bundle.js:27084:35)
原代码
Map组件(loc.js)
import React, { useState, useEffect } from 'react'; import { MapContainer, Marker, GoogleApiWrapper } from 'google-maps-react'; import { Loader } from '@googlemaps/js-api-loader'; const Map = ({ google }) => { const [addresses, setAddresses] = useState([ '1600 Amphitheatre Parkway, Mountain View, CA', '1 Infinite Loop, Cupertino, CA', '350 Fifth Avenue, New York, NY', ]); const [coordinates, setCoordinates] = useState([]); useEffect(() => { const loader = new Loader({ apiKey: 'YOUR_API', version: 'weekly', libraries: ['places'], }); loader.load().then(() => { const geocoder = new google.maps.Geocoder(); const fetchCoordinates = async () => { const coordinates = []; for (let i = 0; i < addresses.length; i++) { const result = await new Promise((resolve, reject) => { geocoder.geocode({ address: addresses[i] }, (result, status) => { if (status === 'OK') { resolve(result[0].geometry.location); } else { reject(status); } }); }); coordinates.push(result); } setCoordinates(coordinates); }; fetchCoordinates(); }); }, [addresses, google.maps.Geocoder, google.maps.places]); return ( <MapContainer google={google} center={{ lat: 37.7749, lng: -122.4194 }} zoom={10}> {coordinates.map((coordinate, index) => ( <Marker key={index} position={{ lat: coordinate.lat(), lng: coordinate.lng() }} /> ))} </MapContainer> ); }; export default GoogleApiWrapper({ apiKey: 'YOUR_API', })(Map);
App.js
import { GoogleApiWrapper } from 'google-maps-react'; import Map from './components/locator/loc'; function App(props) { return ( <div className="App"> <Map google={props.google} /> </div> ); } export default GoogleApiWrapper({ apiKey: 'YOUR_API', })(App);
错误原因及修复方案
1. 组件名称混淆
google-maps-react库中没有MapContainer组件,正确的地图容器组件是Map,你混淆了它和其他Google Maps React库的组件名称。
2. 重复加载API
同时使用GoogleApiWrapper和@googlemaps/js-api-loader两种API加载方式,会导致重复初始化引发冲突,只需保留一种即可,推荐用google-maps-react的包装器。
3. 重复包装组件
App.js和Map组件都用GoogleApiWrapper包装,导致Google Maps API多次初始化,且传递google props时出现冲突,只需在一个组件上包装即可。
4. useEffect依赖项错误
google.maps.Geocoder和google.maps.places不适合作为依赖项,直接依赖传入的google props即可,它是稳定的引用。
修改后的代码
Map组件(loc.js)
import React, { useState, useEffect } from 'react'; import { Map, Marker, GoogleApiWrapper } from 'google-maps-react'; const MapComponent = ({ google }) => { const [addresses, setAddresses] = useState([ '1600 Amphitheatre Parkway, Mountain View, CA', '1 Infinite Loop, Cupertino, CA', '350 Fifth Avenue, New York, NY', ]); const [coordinates, setCoordinates] = useState([]); useEffect(() => { if (!google) return; const geocoder = new google.maps.Geocoder(); const fetchCoordinates = async () => { const coords = []; for (const addr of addresses) { try { const result = await new Promise((resolve, reject) => { geocoder.geocode({ address: addr }, (results, status) => { if (status === google.maps.GeocoderStatus.OK) { resolve(results[0].geometry.location); } else { reject(new Error(`地址解析失败 ${addr}: ${status}`)); } }); }); coords.push(result); } catch (err) { console.error(err); } } setCoordinates(coords); }; fetchCoordinates(); }, [addresses, google]); return ( <Map google={google} center={{ lat: 37.7749, lng: -122.4194 }} zoom={10} style={{ width: '100%', height: '80vh' }} > {coordinates.map((coord, index) => ( <Marker key={index} position={{ lat: coord.lat(), lng: coord.lng() }} /> ))} </Map> ); }; export default GoogleApiWrapper({ apiKey: 'YOUR_API', libraries: ['places'] // 在这里指定需要的地图库 })(MapComponent);
App.js
import Map from './components/locator/loc'; function App() { return ( <div className="App"> <Map /> </div> ); } export default App;
额外说明
- 把原组件名
Map改成MapComponent,避免和导入的Map组件重名,也可以直接修改导入名称。 - 移除了
@googlemaps/js-api-loader依赖,直接通过GoogleApiWrapper加载API并指定需要的places库。 - 给
Map组件添加基础样式,确保地图能正常显示。 - 增加错误捕获,避免单个地址解析失败导致整个流程中断。
内容的提问来源于stack exchange,提问作者Taha Habib

