Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React-Google-Maps displays two markers, one at the original position and another marker that follows the new center of the map onDrag

I am using react-google-maps to show the location of the user after fetching her co-ordinates using ReactJS. But, when I move marker from the original position, the initial marker stays there, so two markers are created. I don't know how to fix this.

I want that whatever happens, the Map Marker remains at center, also when the user drags OR zoom in/out of the marker, the marker remains at center, so that the user's location is always at the center of the map. In this way, the user will be able to update her location. As per your example, when I drag, the marker stays fixed in it's location This component was designed so that the user can set her location, with a little bit of tweaking the Marker's position, in case the marker is a little bit off Any Help will be appreciated:

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));

Also See: CodeSandbox Link

like image 747
bucky roberts Avatar asked Sep 15 '20 19:09

bucky roberts


People also ask

What is react Google Maps?

google-map-react is a component written over a small set of the Google Maps API. It allows you to render any React component on the Google Map. It is fully isomorphic and can render on a server. Additionally, it can render map components in the browser even if the Google Maps API is not loaded.


1 Answers

I believe what is happening here is you have set your marker position to center so whenever you drag, a second marker will be generated.

Instead, the react-google-maps docs show an option where you can hard code the latitude and longitude to the marker. Benefits: no duplicate marker. Con: If the user enters a different address that requires the marker to move, you'll need to write an update function.

Change these lines and your issue should be resolved:

initialize hook

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()


  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);
  }

return google map

          <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 responded with a clarification that <Marker /> should actually move with the center of the screen, and the issue is that there is a duplicate.

After much debugging, I found the error is due to the way the element is rendered. Change:

index.js

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

import App from "./App";

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

And your issue is resolved. This also works:

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

import App from "./App";

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

There is a warning in your console about strict mode, and turning it off seems to fix your issue and is the root of the reason why your GoogleMap component was not working as intended:

Warning: Legacy context API has been detected within a strict-mode tree. The old API will be supported in all 16.x releases, but applications using it >should migrate to the new version. Please update the following components: GoogleMap, Marker Learn more about this warning here: ... in StrictMode (at src/index.js:8)


I also found another StackOverflow question that your code was modeled from, so I am going to link here for future viewers as well:

  • https://stackoverflow.com/a/56138599/14198287
  • Working MRE: https://stackblitz.com/edit/react-fhgr5w?file=index.js
like image 122
Harley Lang Avatar answered Oct 06 '22 00:10

Harley Lang