使用GoogleMap API展示自定义位置时,locations数组两处输出不一致求助
问题描述
我导入了一个名为data的JSON对象数组,数组中的对象包含address属性。我计划通过Google Map API查询address对应的坐标,生成名为locations的数组,以此创建自定义Marker。但通过console.log发现,useEffect内的fetchLocations函数与getCoordinates函数中,同一个locations变量的输出值却不相同。请问我哪里出现了错误?
import React, { useEffect, useState } from 'react' import { GoogleMap, useLoadScript, InfoWindowF, MarkerF } from "@react-google-maps/api" import { data } from './data' const API_KEY = "1234567890" export default function MyComponent() { const { isLoaded }: any = useLoadScript({ googleMapsApiKey: API_KEY, }) const result : any[] = []; const [locations, setLocations] = useState(result); useEffect(() => { const fetchLocations = async () => { const locations = await Promise.all(data.map(getCoordinates)); console.log("Locations in fetchLocations", locations) }; fetchLocations(); }, []); const getCoordinates = async (obj: any) => { const objData = await fetch("https://maps.googleapis.com/maps/api/geocode/json?address=" + obj.address + '&key=' + API_KEY) const response = await objData.json() const latitude = response.results[0].geometry.location.lat; const longitude = response.results[0].geometry.location.lng; const position = { 'lat': latitude, 'lng': longitude } locations.push(position) console.log("Locations in get Cooordinate", locations) } if (!isLoaded) return <div>Loading...</div> return ( <div className='flex flex-col'> <GoogleMap zoom={11} center={{ lat: 35.685661305110415, lng: 139.75255896834707 }} mapContainerClassName='w-full h-[32em] m-2 rounded-lg' > <> {locations.map(location=>{ return( <> <MarkerF position={{ lat: location.lat, lng: location.lng }} /> </> ) }) } </> </GoogleMap> <button onClick={() => { setSelected(!selected) }}>Click</button> </div> ) }
错误分析与修复方案
核心问题
1. 变量作用域混乱
你同时存在三个互不关联的locations变量:
- 组件状态的
locations(通过useState声明) fetchLocations里的局部变量locations(const locations = await Promise.all(...))getCoordinates里操作的组件状态locations
这三个变量完全独立,自然日志输出不一样,而且fetchLocations里的locations因为getCoordinates没有返回值,最终是全undefined的数组。
2. 直接修改React状态
React状态是不可变的,直接调用locations.push(position)不会触发组件重新渲染,还会导致状态与UI不同步,异步操作下日志也会出现混乱。
3. Promise.all使用错误
Promise.all需要每个异步函数返回Promise结果,但你的getCoordinates没有返回值,所以最终得到的是一堆undefined。
修复后的代码
import React, { useEffect, useState } from 'react' import { GoogleMap, useLoadScript, MarkerF } from "@react-google-maps/api" import { data } from './data' const API_KEY = "1234567890" export default function MyComponent() { const { isLoaded } = useLoadScript({ googleMapsApiKey: API_KEY, }) // 直接初始化空数组作为状态 const [locations, setLocations] = useState([]); // 补充原代码缺失的selected状态 const [selected, setSelected] = useState(false); useEffect(() => { const fetchLocations = async () => { // 让getCoordinates返回坐标对象,Promise.all收集所有结果 const fetchedLocations = await Promise.all(data.map(getCoordinates)); // 过滤掉获取失败的null值 const validLocations = fetchedLocations.filter(Boolean); // 用setLocations更新状态 setLocations(validLocations); console.log("最终获取的坐标数组", validLocations) }; fetchLocations(); }, []); // 修改getCoordinates,专注于返回单个坐标,不直接操作状态 const getCoordinates = async (obj) => { // 编码地址,避免特殊字符导致请求失败 const encodedAddress = encodeURIComponent(obj.address); try { const objData = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=${encodedAddress}&key=${API_KEY}`); const response = await objData.json(); // 处理无结果的情况 if (!response.results?.length) { console.error(`地址${obj.address}无法获取坐标`); return null; } const { lat, lng } = response.results[0].geometry.location; const position = { lat, lng }; console.log(`地址${obj.address}对应的坐标`, position) return position; } catch (err) { console.error(`请求地址${obj.address}坐标失败`, err); return null; } } if (!isLoaded) return <div>Loading...</div> return ( <div className='flex flex-col'> <GoogleMap zoom={11} center={{ lat: 35.685661305110415, lng: 139.75255896834707 }} mapContainerClassName='w-full h-[32em] m-2 rounded-lg' > {/* 列表渲染必须加key */} {locations.map((location, index) => ( <MarkerF key={index} position={location} /> ))} </GoogleMap> <button onClick={() => { setSelected(!selected) }}>Click</button> </div> ) }
关键修复点
- 让
getCoordinates返回单个坐标对象,不再直接操作状态,Promise.all就能正确收集所有结果。 - 用
setLocations统一更新状态,触发组件重新渲染,保证UI和状态同步。 - 给地址做URL编码,避免特殊字符导致API请求失败。
- 增加错误处理,防止地址无结果或请求失败时组件崩溃。
- 补充原代码缺失的
selected状态,修复按钮点击报错。 - 给
MarkerF添加key属性,符合React列表渲染规范。
内容的提问来源于stack exchange,提问作者kawa
相关产品推荐
相关产品推荐

