You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@react-google-maps/api无法同时渲染两个标记问题求助

解决@react-google-maps/api多标记渲染时source状态异常清空问题

问题根源

  1. 错误的空值判断逻辑:代码中多处使用source.length != []、destination.length != []判断状态是否存在,但source和destination是包含lat/lng/label的对象,并非数组,不存在length属性。这种判断会导致逻辑混乱:当source存在时,source.length为undefined,与[]比较结果为true;但如果source被设为空对象或undefined,该判断依然返回true,进而尝试渲染标记时读取source.lat等属性,导致标记不显示,看起来像是source被清空。
  2. useEffect逻辑冗余:两个独立的useEffect分别监听source和destination,重复调用directionRoute,且未正确处理两者同时存在的条件。

修复方案

1. 修正空值判断

将所有基于length的错误判断替换为针对对象属性的存在性检查:

原错误判断:

if (source.length != [] && destination.length != [])

替换为:

if (source && destination && source.lat && source.lng && destination.lat && destination.lng)

渲染标记时的判断同步修改:

{source && source.lat && source.lng ? (
  <MarkerF ... />
) : null}

2. 合并useEffect逻辑

将两个监听source和destination的useEffect合并为一个,同时监听两者变化,避免重复触发逻辑:

useEffect(() => {
  // 处理地图中心定位
  if (map) {
    if (source?.lat && source?.lng) {
      map.panTo({ lat: source.lat, lng: source.lng });
      setCenter({ lat: source.lat, lng: source.lng });
    }
    if (destination?.lat && destination?.lng) {
      setCenter({ lat: destination.lat, lng: destination.lng });
    }
  }

  // 调用路线规划
  if (source?.lat && source?.lng && destination?.lat && destination?.lng) {
    directionRoute();
  }
}, [source, destination, map]);

3. 检查Context实现

确认SourceContext和DestinationContext的实现,确保setDestination方法不会意外修改source状态,比如是否在组件中误调用了setSource清空值。

修复后的完整代码

"use client";
import React, { useContext, useEffect, useState } from "react";
import {
  DirectionsRenderer,
  GoogleMap,
  MarkerF,
  OverlayView,
  OverlayViewF,
} from "@react-google-maps/api";
import { SourceContext } from "../../context/SourceContext";
import { DestinationContext } from "../../context/DestinationContext";

function Map() {
  const containerStyle = {
    width: "100%",
    height: window.innerWidth * 0.8,
  };

  const { source, setSource } = useContext(SourceContext);
  const { destination, setDestination } = useContext(DestinationContext);

  const [center, setCenter] = useState({
    lat: -3.745,
    lng: -38.523,
  });
  const [map, setMap] = React.useState(null);
  const [directionRoutePoints, setDirectionRoutePoints] = useState([]);

  useEffect(() => {
    if (map) {
      // 优先定位到source,若destination存在则更新中心
      if (source?.lat && source?.lng) {
        map.panTo({ lat: source.lat, lng: source.lng });
        setCenter({ lat: source.lat, lng: source.lng });
      }
      if (destination?.lat && destination?.lng) {
        setCenter({ lat: destination.lat, lng: destination.lng });
      }
    }

    // 当两者都存在时调用路线规划
    if (source?.lat && source?.lng && destination?.lat && destination?.lng) {
      directionRoute();
    }
  }, [source, destination, map]);

  const directionRoute = () => {
    const DirectionsService = new google.maps.DirectionsService();
    DirectionsService.route(
      {
        origin: { lat: source.lat, lng: source.lng },
        destination: { lat: destination.lat, lng: destination.lng },
        travelMode: google.maps.TravelMode.DRIVING,
      },
      (result, status) => {
        if (status === google.maps.DirectionsStatus.OK) {
          setDirectionRoutePoints(result);
        } else {
          console.error("路线规划失败:", status);
        }
      }
    );
  };

  return (
    <GoogleMap
      mapContainerStyle={containerStyle}
      center={center}
      zoom={11}
      onLoad={(map) => setMap(map)}
      options={{ mapId: "4113717585f11867" }}
    >
      {/* 渲染起点标记 */}
      {source?.lat && source?.lng && (
        <MarkerF
          position={{ lat: source.lat, lng: source.lng }}
          icon={{
            url: "/source.png",
            scaledSize: {
              width: 20,
              height: 20,
            },
          }}
        >
          <OverlayViewF
            position={{ lat: source.lat, lng: source.lng }}
            mapPaneName={OverlayView.OVERLAY_MOUSE_TARGET}
          >
            <div className="p-2 bg-white font-bold inline-block">
              <p className="text-black text-[18px]">{source.label}</p>
            </div>
          </OverlayViewF>
        </MarkerF>
      )}

      {/* 渲染终点标记 */}
      {destination?.lat && destination?.lng && (
        <MarkerF
          position={{ lat: destination.lat, lng: destination.lng }}
          icon={{
            url: "/dest.png",
            scaledSize: {
              width: 20,
              height: 20,
            },
          }}
        >
          <OverlayViewF
            position={{ lat: destination.lat, lng: destination.lng }}
            mapPaneName={OverlayView.OVERLAY_MOUSE_TARGET}
          >
            <div className="p-2 bg-white font-bold inline-block">
              <p className="text-black text-[18px]">{destination.label}</p>
            </div>
          </OverlayViewF>
        </MarkerF>
      )}

      {/* <DirectionsRenderer
        directions={directionRoutePoints}
        options={{
          polylineOptions: {
            strokeColor: "#000",
            strokeWeight: 5,
          },
          suppressMarkers: true,
        }}
      /> */}
    </GoogleMap>
  );
}

export default Map;

内容的提问来源于stack exchange,提问作者Ishfat Abrar Islam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 05:37:03