5

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

Harley Lang
  • 2,063
  • 2
  • 10
  • 26
bucky roberts
  • 339
  • 3
  • 9

1 Answers1

8

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:

Harley Lang
  • 2,063
  • 2
  • 10
  • 26
  • 1
    He has set marker position to 'center', So that whenever the user scrolls, the marker remains at center. The marker remains at center very well, but why another marker? Which is not at center? Also after applying your solution, the problem still persists. – Shivam Jha Sep 16 '20 at 03:39
  • @buckyroberts can you confirm that you want to only zoom in / out on the user's location? The function shows dragging functionality, which would indicate you want to let users move about the map rather than zoom in / out on their current location. Shivam Jha see this Sandbox, this has my solutions and does solve OP's question: https://codesandbox.io/s/react-google-maps-demo-forked-stwgm – Harley Lang Sep 16 '20 at 03:48
  • @HarleyLang 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. By the way, Thank you so much for efforts. I appreciate that man! – bucky roberts Sep 16 '20 at 04:20
  • @buckyroberts can you update this question with these criteria? I will take a look at the component docs sometime tomorrow to see if I can create that functionality but if you beat me to it, go ahead and post your answer! – Harley Lang Sep 16 '20 at 04:22
  • Sure, I'll update the question accordingly @HarleyLang. And, I'll update you with an answer, if I can get it to fix. Until then, if you came up with a solution, please update me – bucky roberts Sep 16 '20 at 05:33
  • @HarleyLang any update where you are? Unfortunately, I haven't been able to find the answer until now – bucky roberts Sep 23 '20 at 03:29
  • 2
    @HarleyLang Thank you sooo much man!! I can't thank you enough bro. You just solved a problem tha I was working on a month now. God bless you – bucky roberts Sep 25 '20 at 15:57
  • @buckyroberts my pleasure, I'm glad to hear that helped :) – Harley Lang Sep 25 '20 at 16:30