首页 > 解决方案 > 种族条件?在自定义 Google 地图组件中

问题描述

这是该站点的实时链接,因此您可以看到正在发生的事情。

我需要帮助来确定比赛条件。当我从笔记本电脑(2015 Macbook Air)访问该站点时,有时会出现以下错误:

Uncaught (in Promise)
{
  message: "Map: Expected mapDiv of type Element but was passed null.",
  name: "InvalidValueError"
}

或者

InvalidValueError: setCenter: 
  not a LatLng or LatLngLiteral with finite coordinates: 
  in property lat: NaN is not an accepted value.

在我清除 Chrome 中的所有浏览数据后,大约有 50% 的机会发生这些错误。再次刷新或加载站点后,不会发生错误(除了 e.getDraggable 不是一个函数,但我现在并不担心那个。)

我试图“异步化”(正在申请专利)该组件以消除竞争条件。我还尝试在组件上设置加载状态,并且仅在所有内容加载后才渲染地图。我采取的另一种方法是向<script>我们从谷歌地图 API 插入的内容添加一个 onLoad。

以下是组件,非常欢迎所有建议!

import React, { useEffect, useRef } from "react";
import { connect } from "react-redux";
import { thunks as AiportStore } from "../../store/airports";
import { api } from "../../config";
import {
  thunks as FlightPathStoreThunks,
  actions as FlightPathStoreActions,
} from "../../store/flightPath";
import "./mapInfoWindow.css";
// Variables
let key = “&lt;< key >>";
let myLocation = {};
if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(function (position) {
    myLocation = {
      lat: position.coords.latitude,
      lng: position.coords.longitude,
    };
  });
} else {
  myLocation = {
    // CN Tower Landmark
    lat: 32.755489,
    lng: -97.330765,
  };
}
// styles
const mapStyles = {
  width: "inherit",
  height: "inherit",
};

function GoogleMaps({
  airports,
  updateAirportCoords,
  flightPath,
  setStartPoint,
  setEndPoint,
  resetStartEnd,
  startPoint,
  endPoint,
}) {
  // refs
  const googleMapRef = React.createRef();
  const googleMap = useRef(null);
  const infoWindow = useRef(null);
  const Polyline = useRef(null);
  const addStartButton = useRef(null);
  const addEndButton = useRef(null);

  useEffect(() => {
    if (Polyline.current) {
      Polyline.current.setMap(null);
    }
    if (flightPath.length > 0) {
      if (Polyline.current) {
        Polyline.current.setMap(null);
      }
      Polyline.current = new window.google.maps.Polyline({
        path: flightPath,
        geodesic: true,
        strokeColor: "#FF0000",
        strokeOpacity: 1.0,
        strokeWeight: 2,
      });
      Polyline.current.setMap(googleMap.current);
    }
  }, [JSON.stringify(flightPath)]);

  useEffect(() => {
    (async () => {
      if (airports.length > 0) {
        createMarker();
      } else if (airports.length == 0) {
        const googleMapScript = document.createElement("script");
        googleMapScript.src = `https://maps.googleapis.com/maps/api/js?key=${key}&libraries=places`;
        googleMapScript.async = true;
        window.document.body.appendChild(googleMapScript);
  
        //clusterer add on
        const clustererScript = document.createElement("script");
        clustererScript.src =
          "https://unpkg.com/@google/markerclustererplus@4.0.1/dist/markerclustererplus.min.js";
        window.document.body.appendChild(clustererScript);
  
        googleMapScript.addEventListener("load", async () => {
          googleMap.current = await createGoogleMap();
  
          getAirportCoords();
        });
      }
    })();
  }, [JSON.stringify(airports)]);
  // helper functions
  function createDomNode(airport) {
    //div containing all info in the popup
    const infoWindowDiv = document.createElement("div");
    infoWindowDiv.innerHTML = `
    <div class="info-window">
        <div class="info-window__top-info info-window__section">
            <div class="info-window__section-title">
                <div>${airport.data.name}</div>
            </div>
            <div><b>location:</b> ${airport.data.city}, ${
      airport.data.state
    }</div>
            <div><b>lat, lng:</b> ${parseFloat(airport.data.lat)}, ${
      parseFloat(airport.data.lon)
    })</div>
        </div>

        <div class="info-window__middle-info info-window__section">
            <div class="info-window__section-title">
                <div>Airport Info</div>
            </div>
            <div><b>Location Id:</b> ${airport.data.loc_id}</div>
            <div><b>CTAF:</b> ${airport.data.ctaf || "None"}</div>
            <div><b>Sectional Chart:</b> ${airport.data.sectional_chart}</div>
            <div><b>Eleveation:</b> ${airport.data.elevation}</div>
            <div><b>ATC Tower:</b> ${airport.data.atc_tower || "None"}</div>
            <div><b>Landing Fee:</b> ${airport.data.landing_fee || "None"}</div>
        </div>

        <div class="info-window__bottom-info info-window__section">
            <div class="info-window__section-title">
                <div>Contact Info</div>
            </div>
            <div><b>FSS Phone Number</b>: ${
              airport.data.fss_phone_number || "None"
            }</div>
            <div><b>Manager Name:</b> ${airport.data.manager_name}</div>
            <div><b>Manager Phone Number:</b> ${
              airport.data.manager_phone_number
            }</div>
        </div>
    </div>
    `;

    //button to add start of flight path
    addStartButton.current = document.createElement("button");
    addStartButton.current.innerHTML = "Add to Start";

    addStartButton.current.addEventListener("click", () => {
      // set start coords to redux store
      setStartPoint(airport.data);
      // addStartButton.current.setAttribute('style', 'display:none')

      // startButtonPressed = true;
    });
    infoWindowDiv.appendChild(addStartButton.current);

    //button to add end of flight path
    addEndButton.current = document.createElement("button");
    addEndButton.current.innerHTML = "Add to End";

    addEndButton.current.addEventListener("click", () => {
      setEndPoint(airport.data);
      // addEndButton.current.setAttribute('style', 'display:none')
      // endButtonPressed = true;
    });
    infoWindowDiv.appendChild(addEndButton.current);

    //button to reset start and end of flight path
    const resetButton = document.createElement("button");
    resetButton.innerHTML = "Reset Start and End";
    resetButton.addEventListener("click", () => {
      //reset coords in redux store
      resetStartEnd();
      addStartButton.current.setAttribute("style", "display:inline-block");
      addEndButton.current.setAttribute("style", "display:inline-block");
    });
    infoWindowDiv.appendChild(resetButton);

    return infoWindowDiv;
  }

  const createGoogleMap = async () =>
    await new window.google.maps.Map(googleMapRef.current, {
      zoom: 14,
      center: {
        lat: myLocation.lat,
        lng: myLocation.lng,
      },
    });

  const createMarker = async () => {
    infoWindow.current = await new window.google.maps.InfoWindow({
      content: "<div></div>",
    });
    // create markers
    let markers = airports.map(async (airport) => {
      let marker = await new window.google.maps.Marker({
        position: {
          lat: parseFloat(airport.lat),
          lng: parseFloat(airport.lng),
        },
        map: googleMap.current,
      });

      marker.addListener("click", async function (e) {
        //rendering multiple windows because creating a new window obj for each marker

        const markerLat = e.latLng.lat();
        const markerLng = e.latLng.lng();

        const data = { lat: parseFloat(markerLat), lng: parseFloat(markerLng) };
        const airportData = await fetch(`${api}/airports/`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(data),
        });

        const airport = await airportData.json();
        infoWindow.current.setContent(createDomNode(airport));

        infoWindow.current.open(googleMap.current, marker);
      });

      return marker;
    });

    new window.MarkerClusterer(googleMap.current, markers, {
      imagePath:
        "https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m",
    });
  };

  async function getAirportCoords() {
    updateAirportCoords();
    // updateFlightPath();
  }
  return <div id="google-map" ref={googleMapRef} style={mapStyles} />;
}

const mapStateToProps = (state) => {
  return {
    airports: state.airports.airports || [],
    flightPath: state.flightPath.flightPath || [],
    startPoint: state.flightPath.startPoint || {},
    endPoint: state.flightPath.endPoint || {},
    showEnd: state.flightPath.endButtonPressed || false,
    showStart: state.flightPath.startButtonPressed || false,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    setStartPoint: (value) =>
      dispatch(FlightPathStoreActions.setStartPoint(value)),
    setEndPoint: (value) => dispatch(FlightPathStoreActions.setEndPoint(value)),
    setStart: (value) => dispatch(FlightPathStoreActions.setStart(value)),
    setEnd: (value) => dispatch(FlightPathStoreActions.setEnd(value)),
    resetStartEnd: () => dispatch(FlightPathStoreActions.resetStartEnd()),
    resetFlightPath: () => dispatch(FlightPathStoreActions.resetFlightPath()),
    updateAirportCoords: () => dispatch(AiportStore.updateAirportCoords()),
    updateFlightPath: () => dispatch(FlightPathStoreThunks.updateFlightPath()),
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(GoogleMaps);

标签: javascriptreactjsgoogle-mapsgoogle-maps-api-3

解决方案


推荐阅读