React-Google-Maps 显示两个标记,一个在原始位置,另一个标记在地图的新中心后 onDrag
React-Google-Maps displays two markers, one at the original position and another marker that follows the new center of the map onDrag
我使用 react-google-maps 在使用 ReactJS 获取坐标后显示用户的位置。但是,当我从原始位置移动标记时,初始标记会保留在那里,因此会创建两个标记。我不知道如何解决这个问题。
我希望无论发生什么,地图标记都保持在中心,而且当用户拖动或缩放标记 in/out 时,标记保持在中心,这样用户的位置总是在中心的地图。这样,用户将能够更新她的位置。根据你的例子,当我拖动时,标记保持固定在它的位置 这个组件的设计是为了让用户可以设置她的位置,稍微调整标记的位置,以防标记有点偏离
任何帮助将不胜感激:
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));
我相信这里发生的事情是您将标记位置设置为 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);
}
return 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
组件未按预期工作的根本原因:
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)
我还发现了另一个 Whosebug 问题,您的代码是从中建模的,所以我也将在此处 link 为未来的观众提供帮助:
我使用 react-google-maps 在使用 ReactJS 获取坐标后显示用户的位置。但是,当我从原始位置移动标记时,初始标记会保留在那里,因此会创建两个标记。我不知道如何解决这个问题。
我希望无论发生什么,地图标记都保持在中心,而且当用户拖动或缩放标记 in/out 时,标记保持在中心,这样用户的位置总是在中心的地图。这样,用户将能够更新她的位置。根据你的例子,当我拖动时,标记保持固定在它的位置 这个组件的设计是为了让用户可以设置她的位置,稍微调整标记的位置,以防标记有点偏离 任何帮助将不胜感激:
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));
我相信这里发生的事情是您将标记位置设置为 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);
}
return 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
组件未按预期工作的根本原因:
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)
我还发现了另一个 Whosebug 问题,您的代码是从中建模的,所以我也将在此处 link 为未来的观众提供帮助: