React中@react-google-maps/api地图标记不显示问题求助
问题排查与解决方案
核心问题
- 普通变量无法触发重渲染:当前
markers是普通数组,异步生成坐标后push操作不会被React感知,导致Map组件接收的始终是初始空数组——只有修改Map组件代码触发热更新时,才会读取到已填充的数组。 - 异步逻辑时机错误:在组件渲染阶段直接调用异步的
generateLatLng,不会等待所有坐标生成完成就渲染Map,且完成后无状态更新触发重渲染。 - useState误用导致无限重渲染:之前直接在循环中调用
setMarkers,每次状态更新都会触发组件重渲染,进而再次执行循环,陷入死循环。
修复方案
- 用
useState存储markers状态,确保状态更新能触发组件重渲染。 - 用
useEffect监听properties和isLoaded的变化,仅当数据和地图API都准备好时,批量处理坐标生成。 - 使用
Promise.all等待所有坐标异步请求完成后,一次性更新markers,避免多次状态更新导致的不必要重渲染。
修改后的PropertyPage代码
import React, { useState, useEffect } from 'react' import PropertyCard from './PropertyCard' import RingLoader from 'react-spinners/RingLoader'; import Map from './Map'; import { Grid, Button, Image } from 'semantic-ui-react' import { Link } from 'react-router-dom'; import { useLoadScript } from '@react-google-maps/api' import { getGeocode, getLatLng } from 'use-places-autocomplete'; import { useGetPropertiesQuery } from '../../app/services/propertiesAPI' const libraries = ['places'] const PropertyPage = () => { const { data: properties = [], isLoading, isError, error, isSuccess } = useGetPropertiesQuery() const { isLoaded } = useLoadScript({ googleMapsApiKey: process.env.REACT_APP_GOOGLEMAPS_KEY, libraries, }); const [markers, setMarkers] = useState([]); if (!isLoaded) { return <div>Map is loading...</div> } let content const generateLatLng = async (address) => { try { const results = await getGeocode({ address }); if (results.length === 0) { return null; } const { lat, lng } = await getLatLng(results[0]); return lat && lng ? { lat, lng } : null; } catch (error) { console.error('Error while generating lat and lng:', error); return null; } }; // 批量生成坐标并更新markers useEffect(() => { const fetchMarkers = async () => { if (!isSuccess || properties.length === 0) return; // 并行请求所有坐标 const markerPromises = properties.map(property => generateLatLng(property.address)); const results = await Promise.all(markerPromises); // 过滤掉无效结果 const validMarkers = results.filter(marker => marker !== null); setMarkers(validMarkers); }; fetchMarkers(); }, [isSuccess, properties]); // 仅当数据就绪或属性列表变化时执行 if (isLoading) { content = <h1>Loading...</h1> } else if (isError) { content = <div>{error.toString()}</div> } if (isSuccess && properties.length > 0) { content = properties.map((property) => <PropertyCard key={property.id} property={property}/>) } const noPropertiesYetMessage = ( <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}> <h1>You have no properties yet.</h1> <Button as={Link} to='/properties/add-new'> Add More </Button> <br></br> <div style={{ width: '50rem', height: '30rem', position: 'relative', overflow: 'hidden' }}> <Image fluid style={{ width: '100%', height: '100%', objectFit: 'cover' }} src='https://www.bhg.com/thmb/TXKtfIyA_UUCOWtMsLXetN5Wvb8=/1493x0/filters:no_upscale():strip_icc()/gray-exterior-white-trim-path-09767f9e-83bad323e9d2473b90e69add9165c12f.jpg' alt='building entrance'/> </div> </div> ) return ( <div> <RingLoader color={'#F5A623'} loading={isLoading} /> <div className='ui container hidden divider'> { properties && properties.length > 0 ? <Grid Columns={3}> <Grid.Column width={2}> <Button as={Link} to='/properties/add-new' style={{ marginTop: '10px' }}> Add More </Button> <Image src='https://wdy.h-cdn.co/assets/16/05/480x480/square-1454612525-baby-pandas.jpg' size='tiny' circular style={{ marginTop: '10px' }}/> </Grid.Column> { content } </Grid> : (noPropertiesYetMessage)} <Grid.Row style={{width: '100vw', height: '100vh', paddingTop: '15px'}}> <Map markers={markers}/> </Grid.Row> </div> </div> ) } export default PropertyPage
Map组件无需修改
原Map组件逻辑正确,当markers状态更新后,会自动重新渲染所有标记。
内容的提问来源于stack exchange,提问作者sbasken
相关产品推荐
相关产品推荐

