React-Google-Maps显示两个标记,一个位于原始位置,另一个标记位于onDrag上新的地图中心

问题描述

我正在使用react-google-maps显示用户的位置,该位置是使用ReactJS提取用户的坐标后得出的。但是,当我从原始位置移动标记时,初始标记停留在那里,因此创建了两个标记。我不知道该如何解决

无论发生什么事情,我都希望地图标记保持在中心,而且当用户拖动或放大/缩小标记时,标记也将保持在中心,以便用户的位置始终位于地图的中心。这样,用户将能够更新她的位置。以您的示例为例,当我拖动鼠标时,标记保持固定在该位置。该组件的设计目的是使用户可以设置她的位置,并稍稍调整标记的位置,以防标记略有偏离 任何帮助将不胜感激:

App.js

import React from "react";
import WrappedMap from "./Map";
import "./styles.css";

export default class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      location: "",place: ""
    };
  }

  handleLocation = (location) => {
    this.setState({ location: location });
  };

  handlePlace = (place) => {
    this.setState({ place: place });
  };

  render() {
    return (
      <div className="App">
        <WrappedMap
          googleMapURL={`https://maps.googleapis.com/maps/api/js?key=`}
          loadingElement={<div style={{ height: `100%` }} />}
          containerElement={
            <div
              style={{
                height: `50%`,width: "95%",position: "absolute",marginTop: "25%"
              }}
            />
          }
          mapElement={<div style={{ height: `100%` }} />}
          location={this.state.location}
          handleLocation={this.handleLocation}
          changeState={this.changeState}
          place={this.state.place}
          handlePlace={this.handlePlace}
          handleUseGPS={this.handleUseGPS}
        />
      </div>
    );
  }
}

Map.js

import React,{ useRef,useState,useEffect } from "react";
import Geocode from "react-geocode";
import Button from "@material-ui/core/Button";
import {
  GoogleMap,withScriptjs,withGoogleMap,Marker
} from "react-google-maps";
// import "./Sign.css";
function Map({
  location,handleLocation,changeState,place,handlePlace,handleUseGPS
}) {
  const [center,setCenter] = useState(location);
  const [showMap,setShowMap] = useState(false);
  const refMap = useRef(null);
  var options = {
    enableHighAccuracy: true,timeout: 10000,maximumAge: 30000
  };
  function success(pos) {
    var crd = pos.coords;
    console.log(crd);
    console.log("Your current position is:");
    console.log(`Latitude : ${crd.latitude}`);
    console.log(`Longitude: ${crd.longitude}`);
    console.log(`More or less ${crd.accuracy} meters.`);
    const loc = {
      lat: crd.latitude,lng: crd.longitude
    };
    handleLocation(loc);
    getAndChangeAddress(loc);
    setCenter(loc);
    setShowMap(true);
  }
  function error(err) {
    if (!navigator.geolocation) {
      console.log("Geolocation is not supported by your browser");
    } else {
      console.log("loading");
    }
    let typeErr = err.code;
    console.log(`Code: ${typeErr}`);
    switch (typeErr) {
      case 1:
        console.log("User has not given permissions");
        break;
      case 2:
        console.log(
          "The acquisition of the geolocation Failed because at least one internal source of position returned an internal error."
        );
        break;
      case 3:
        console.log("Timeout reached before obtaining information");
        break;
      default:
        break;
    }
    console.warn(`ERROR(${err.code}): ${err.message}`);
    handlePlace("");
    handleLocation({});
    // handleUseGPS(true);
    // changeState(7);
  }
  const handleBoundsChanged = () => {
    const mapCenter = refMap.current.getCenter(); //get map center
    setCenter(mapCenter);
  };
  useEffect(() => {
    navigator.geolocation.getCurrentPosition(success,error,options);
  },[]);
  const handleDragEnd = () => {
    const newCenter = refMap.current.getCenter();
    const newLocation = {
      lat: newCenter.lat(),lng: newCenter.lng()
    };
    handleLocation(newLocation);
    getAndChangeAddress(newLocation);
  };
  const returnToMenu = () => {
    // changeState(4);
  };
  const getAndChangeAddress = (loc) => {
    const lat = loc.lat.toString();
    const lng = loc.lng.toString();
    console.log(typeof lat);
    console.log(`From getAddress() function => lat: ${lat},lng: ${lng}`);
    Geocode.fromLatLng(lat,lng).then(
      (response) => {
        const address = response.results[0].formatted_address;
        console.log(`Formatted address: ${address}`);
        handlePlace(address);
      },(error) => {
        console.error(error);
        console.log("Error occuredd in getting address");
      }
    );
  };
  return (
    <>
      <div className="sign-in-form">
        {showMap && (
          <GoogleMap
            ref={refMap}
            defaultZoom={15}
            defaultCenter={center}
            onBoundsChanged={handleBoundsChanged}
            onDragEnd={handleDragEnd}
          >
            <Marker
              // defaultPlace={center}
              position={center}
              // ref={refMap}
              // defaultPosition={center}
              // onDrag={handleBoundsChanged}
              // onDragEnd={handleDragEnd}
            />
          </GoogleMap>
        )}
        {location.lat !== "" && (
          <>
            <hr />
            <div style={{ margin: "1em" }}>{place}</div>
            <hr />
          </>
        )}
        <Button
          className="otp-button"
          onClick={returnToMenu}
          fullWidth
          variant="contained"
        >
          SAVE LOCATION
        </Button>
      </div>
    </>
  );
}
export default withScriptjs(withGoogleMap(Map));

另请参见CodeSandbox Link

解决方法

我相信这里发生的事情是您将标记位置设置为center,因此每当拖动时,都会生成另一个标记。

相反,react-google-maps docs显示了一个选项,您可以在其中将纬度和经度硬编码到标记上。好处:没有重复的标记。缺点:如果用户输入的其他地址需要标记移动,则需要编写更新功能。

更改这些行,您的问题应得到解决:

初始化钩子

function Map({
  location,handleLocation,changeState,place,handlePlace,handleUseGPS
}) {
  const [center,setCenter] = useState(location);
  const [showMap,setShowMap] = useState(false);
  const [mylat,setLat] = useState(0);              {/* <------ add this hook */}
  const [mylong,setLong] = useState(0);            {/* <------ and this hook */}
  const refMap = useRef(null);

...

功能成功()


  function success(pos) {
    var crd = pos.coords;
    console.log(crd);
    console.log("Your current position is:");
    console.log(`Latitude : ${crd.latitude}`);
    console.log(`Longitude: ${crd.longitude}`);
    console.log(`More or less ${crd.accuracy} meters.`);

    setLat(crd.latitude);                   {/* <------ set state here*/}
    setLong(crd.longitude);                 {/* <------ set state here*/}

    const loc = {
      lat: crd.latitude,lng: crd.longitude
    };
    handleLocation(loc);
    getAndChangeAddress(loc);
    setCenter(loc);
    setShowMap(true);
  }

返回Google地图

          <GoogleMap
            ref={refMap}
            defaultZoom={15}
            defaultCenter={center}
            onBoundsChanged={handleBoundsChanged}
            onDragEnd={handleDragEnd}
          >
            <Marker
              // defaultPlace={center}
              position={{ lat: mylat,lng: mylong}}        {/* <----- lat and long here */}
              // ref={refMap}
              // defaultPosition={center}
              // onDrag={handleBoundsChanged}
              // onDragEnd={handleDragEnd}
            />
          </GoogleMap>

OP做出了澄清,要求<Marker />实际上应该在屏幕中央移动,问题是存在重复项。

经过大量调试,我发现错误是由于呈现元素的方式引起的。更改:

index.js

import React,{ Component } from 'react';
import { render } from 'react-dom';

import App from "./App";

render(<App />,document.getElementById('root'));

您的问题已解决。这也可以:

import React from "react";
import ReactDOM from "react-dom";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
    <App />,{/* <-------- remove <StrictMode /> */}
  rootElement
);

控制台中出现有关严格模式的警告,将其关闭似乎可以解决您的问题,这是GoogleMap组件未按预期工作的原因的根源:

警告:已在严格模式树中检测到传统上下文API。 所有16.x版本都将支持旧的API,但是使用旧API的应用程序应迁移到新版本。 请更新以下组件:GoogleMap,标记 在此处了解有关此警告的更多信息:... 在StrictMode中(位于src / index.js:8)


我还发现了另一个StackOverflow问题,您的代码是根据该问题建模的,因此我也将在此处链接以供将来的查看者使用: