问题描述
我遵循了 Leaflet Routing Machine regarding interactions(即 onClicks)中的建议。
通过我的实现,我将航点保存在本地存储中——将我从地图点击中获得的纬度和经度 obj 保存到一个名为 markers
的数组
事件处理程序有一个条件,它将点击分为两个结果——添加(到标记数组)或更新它。
就像我在标题中所说的,初始交互很好,只是当我删除任何标记并尝试再次添加时,问题就出现了。我还注意到 markers
数组完全为空,并且下一个触发的事件是一个更新,显然它应该是一个补充:
这是路由机中的相关代码:
class Routing extends MapLayer {
static contextType = UserContextdispatch;
constructor(props) {
super(props);
this.state = {
showSpinner: false,localdispatch: null,};
this.handleLoader = this.handleLoader.bind(this);
this.handleRemoveWayPoint = this.handleRemoveWayPoint.bind(this);
this.handleSetMarker = this.handleSetMarker.bind(this);
}
handleRemoveWayPoint() {
var waypoints = this.control.getWaypoints();
for (let i = waypoints.length - 1; i >= 0; i--) {
console.log('waypoints[i].latLng !== null ',waypoints[i].latLng !== null);
if (waypoints[i].latLng !== null) {
waypoints[i].latLng = null;
break;
}
}
this.control.setWaypoints(waypoints);
}
createLeafletElement(props) {
const { map } = this.props.leaflet;
if (map && !this.control) {
this.control = L.Routing.control({
collapsible: true,show: false,position: 'bottomleft',lineOptions: {
styles: [{ color: 'chartreuse',opacity: 1,weight: 5 }]
},waypoints: [null],createMarker: function(i,wp,nWps) {
if (i === 0) {
return L.marker(wp.latLng,{
icon: startIcon,draggable: true,keyboard: true,alt: 'current location'
}).on('drag',function(e) {
e.latlng.alt = 'current location';
console.log('there be dragons start!!',e);
RoutingMachineRef.handleSetMarker({
...e.oldLatLng,...e.latlng
});
});
}
if (i === nWps - 1) {
return L.marker(wp.latLng,{
icon: endIcon,alt: 'current destination'
}).on('drag',function(e) {
e.latlng.alt = 'current destination';
console.log('there be dragons dest!!',...e.latlng
});
});
}
}
});
L.Routing.errorControl(this.control).addTo(map);
}
return this.control.getPlan();
}
componentDidMount() {
const { map } = this.props.leaflet;
console.log('markers ',markers);
this.setState(prevstate => {
localdispatch: prevstate.localdispatch = this.context.dispatch;
});
map.addControl(this.control);
}
updateLeafletElement(fromProps,toProps) {
const { map } = this.props.leaflet;
var self = this;
self;
var { markers } = this.props;
function createButton(label,container) {
var btn = L.DomUtil.create('button','',container);
btn.setAttribute('type','button');
btn.innerHTML = label;
return btn;
}
var { localdispatch } = this.state;
var container = L.DomUtil.create('div'),startBtn = createButton('Start from this location',container),destBtn = createButton('Go to this location',container);
map.on(
'click',function(e) {
L.popup()
.setContent(container)
.setLatLng(e.latlng)
.openOn(map);
L.DomEvent.on(startBtn,'click',function() {
if (e.latlng) {
e.latlng.alt = 'current location';
console.log('adding);
localdispatch({
type: 'addMarker',payload: {
marker: e.latlng
}
});
}
if (markers.length === 0) {
console.log('updating ');
e.latlng.alt = 'current location';
localdispatch({
type: 'updateMarkers',payload: {
marker: e.latlng
}
});
}
self.control.spliceWaypoints(0,1,e.latlng);
map.closePopup();
});
L.DomEvent.on(
destBtn,function() {
console.log('e',e);
if (markers[1] === undefined) {
e.latlng.alt = 'current destination';
console.log('e.latlng ',e.latlng);
localdispatch({
type: 'addMarker',payload: {
marker: e.latlng
}
});
}
if (toProps.markers[1] !== undefined) {
console.log('updating ');
e.latlng.alt = 'current destination';
localdispatch({
type: 'updateMarkers',payload: {
marker: e.latlng
}
});
}
this.control.spliceWaypoints(1,e.latlng);
map.closePopup();
}.bind(this)
);
}.bind(this)
);
if (toProps.removeRoutingMachine !== false) {
this.control.setWaypoints([]);
}
}
componentwillUnmount() {
this.destroyRouting();
}
destroyRouting() {
const { map } = this.props.leaflet;
if (map) {
map.removeControl(this.control);
}
}
}
export default withLeaflet(Routing);
提前致谢!
解决方法
如您所见,我有一些与 Map
本身中的 RoutingMachine
(航点的 onClick)相关的代码;在考虑之后,我将其作为 Map
移至 handlerFunction
组件。现在它起作用了!
import React,{ useState,useEffect,useRef } from 'react';
import { Button,Dimmer,Loader } from 'semantic-ui-react';
import L from 'leaflet';
import * as ELG from 'esri-leaflet-geocoder';
import Control from 'react-leaflet-control';
// import MapboxLayer from '../MapboxLayer/MapboxLayer.jsx';
import Routing from '../RoutingMachine/RoutingMachine.jsx';
import { parse,stringify } from 'flatted';
import { userState,userDispatch } from '../Context/UserContext.jsx';
import UIContext from '../Context/UIContext.jsx';
function currentMapViewPropsAreEqual(prevProps,nextProps) {
console.log('prevProps,nextProps ',prevProps,nextProps);
console.log(
'prevProps.currentMapView === nextProps.currentMapView && prevProps.Map === nextProps.Map && prevProps.TileLayer === nextProps.TileLayer ',prevProps.currentMapView === nextProps.currentMapView &&
prevProps.Map === nextProps.Map &&
prevProps.TileLayer === nextProps.TileLayer
);
return (
prevProps.currentMapView === nextProps.currentMapView &&
prevProps.Map === nextProps.Map &&
prevProps.TileLayer === nextProps.TileLayer
);
}
function MyMap({ currentMapView,Map,TileLayer }) {
console.log('currentMapView; ',currentMapView);
var [animate,setAnimate] = useState(false);
var [userLocation,setUserLocation] = useState(null);
const [myState,setMyState] = useState(null);
var handleWaypointsOnMapRef = useRef(handleWaypointsOnMap);
var mapRef = useRef();
var mapRefForRoutingMachine = useRef();
var { state } = userState();
var { dispatch } = userDispatch();
var {
currentMap,isRoutingVisible,removeRoutingMachine,isLengthOfMarkersLessThanTwo,markers
} = state;
useEffect(() => {
handleWaypointsOnMapRef.current = handleWaypointsOnMap;
}); // update after each render
useEffect(() => {
var { current = {} } = mapRef;
var { leafletElement: map } = current;
console.log('foo');
console.log('currentMap ',currentMapView);
map.locate({ setView: true });
map.on('locationfound',handleOnLocationFound);
},[]);
useEffect(() => {
var searchControl = new ELG.Geosearch({
useMapBounds: false
});
var { current = {} } = mapRef;
var { leafletElement: map } = current;
console.log('mapRef ',mapRef);
searchControl.addTo(map);
var cb = e => handleWaypointsOnMapRef.current(e); // then use most recent cb value
searchControl.on('results',cb);
if (Object.keys(currentMap).length === 0) {
dispatch({
type: 'setMap',payload: {
currentMap: stringify(map)
}
});
}
return () => {
searchControl.off('results',cb);
};
},[]);
function handleOnClickClearOneMarkerAtTime() {
dispatch({
type: 'setIsRoutingVisible',payload: {
isRoutingVisible: false
}
});
mapRefForRoutingMachine.current.handleRemoveWayPoint();
dispatch({
type: 'deleteUserMarkers'
});
}
function handleOnClickClearAllMarkers() {
mapRefForRoutingMachine.current.handleClearWayPoints();
dispatch({
type: 'resetUserMarkers'
});
}
function handleOnClickMarkerClick(e) {
e.originalEvent.view.L.DomEvent.stopPropagation(e);
}
function handleWaypointsOnMap(e) {
var { current = {} } = mapRef;
var { leafletElement: map } = current;
dispatch({
type: 'setIsRoutingVisible',payload: {
isRoutingVisible: true
}
});
dispatch({
type: 'setRemoveRoutingMachine',payload: {
removeRoutingMachine: false
}
});
function createButton(label,container) {
var btn = L.DomUtil.create('button','',container);
btn.setAttribute('type','button');
btn.innerHTML = label;
return btn;
}
var container = L.DomUtil.create('div'),startBtn = createButton('Start from this location',container),destBtn = createButton('Go to this location',container);
L.popup()
.setContent(container)
.setLatLng(e.latlng)
.openOn(map);
L.DomEvent.on(startBtn,'click',function() {
if (markers.length === 0) {
e.latlng.alt = 'current location';
console.log('adding current location',e.latlng);
dispatch({
type: 'addMarker',payload: {
marker: e.latlng
}
});
}
if (markers[0] != undefined) {
e.latlng.alt = 'current location';
console.log('updating current location',e.latlng);
dispatch({
type: 'updateMarkers',payload: {
marker: e.latlng
}
});
}
mapRefForRoutingMachine.current.handleSpliceWaypoints(0,1,e.latlng);
map.closePopup();
});
L.DomEvent.on(
destBtn,function() {
console.log('e',e);
if (markers.length === 1) {
e.latlng.alt = 'current destination';
console.log('adding destination ',e.latlng);
dispatch({
type: 'addMarker',payload: {
marker: e.latlng
}
});
}
if (markers.length === 2 && markers[1] !== undefined) {
e.latlng.alt = 'current destination';
console.log('updating destination',e.latlng);
dispatch({
type: 'updateMarkers',payload: {
marker: e.latlng
}
});
}
mapRefForRoutingMachine.current.handleSpliceWaypoints(1,e.latlng);
map.closePopup();
}.bind(this)
);
}
function handleOnViewportChanged(e) {
console.log('viewport change',e);
console.log('currentMapView ',currentMapView);
var { current = {} } = mapRef;
var { leafletElement: map } = current;
map.on('zoomend',function() {
var zoom = map.getZoom();
console.log('zoom ',zoom);
console.log("'dispatch setMapZoom'; ");
dispatch({
type: 'setMapZoom',payload: {
currentMapView: zoom
}
});
});
}
function handleOnLocationFound(e) {
console.log('e ',e);
var { current = {} } = mapRef;
var { leafletElement: map } = current;
map.setZoom(currentMapView);
var latlng = e.latlng;
var radius = e.accuracy;
var circle = L.circle(latlng,radius);
circle.addTo(map);
}
return (
<Map
preferCanvas={true}
id="myMap"
animate={animate}
zoom={currentMapView}
ref={mapRef}
onViewportChanged={handleOnViewportChanged}
onClick={e => handleWaypointsOnMap(e)}
>
<TileLayer
url={`https://api.mapbox.com/styles/v1/${process.env.MAPBOX_USERNAME}/${
process.env.MAPBOX_STYLE_ID
}/tiles/256/{z}/{x}/{y}@2x?access_token=${process.env.MAPBOX_ACCESS_TOKEN}`}
attribution='Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors,<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>,Imagery © <a href="https://www.mapbox.com/">Mapbox</a>'
/>
<Control position="bottomleft">
<Button onClick={handleOnClickClearOneMarkerAtTime} color="orange" size="small">
delete one marker!
</Button>
</Control>
<Control position="bottomright">
<Button onClick={handleOnClickClearAllMarkers} color="red" size="small">
clear all!
</Button>
</Control>
{mapRef && (
<Routing
isRoutingVisible={isRoutingVisible}
ref={mapRefForRoutingMachine}
markers={markers}
stringify={stringify}
isLengthOfMarkersLessThanTwo={isLengthOfMarkersLessThanTwo}
removeRoutingMachine={removeRoutingMachine}
userLocation={userLocation}
/>
)}
</Map>
);
}
var MemoizedMyMap = React.memo(MyMap,currentMapViewPropsAreEqual);
export default MemoizedMyMap;
这是路由机器:
import { MapLayer } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet-routing-machine';
import { withLeaflet } from 'react-leaflet';
import UserContextDispatch from '../Context/UserContext.jsx';
import { Dimmer,Loader } from 'semantic-ui-react';
import { isEqual } from 'lodash';
import AwesomeDebouncePromise from 'awesome-debounce-promise';
class Routing extends MapLayer {
static contextType = UserContextDispatch;
constructor(props) {
super(props);
this.state = {
showSpinner: false,localDispatch: null,markerIsBeingDragged: false,currentMarker: {}
};
this.handleLoader = this.handleLoader.bind(this);
this.handleRemoveWayPoint = this.handleRemoveWayPoint.bind(this);
this.handleClearWayPoints = this.handleClearWayPoints.bind(this);
this.handleSpliceWaypoints = this.handleSpliceWaypoints.bind(this);
this.handleSetMarker = this.handleSetMarker.bind(this);
}
handleSetMarker(marker) {
var { markers } = this.props;
if (markers[0] !== undefined && markers[0].alt === 'current location') {
this.setState(prevState => ({
currentMarker: { ...prevState.currentMarker,...marker }
}));
}
if (markers[1] !== undefined && markers[1].alt === 'current destination') {
this.setState(prevState => ({
currentMarker: { ...prevState.currentMarker,...marker }
}));
}
console.log('this.state ',this.state);
}
handleSpliceWaypoints(start,end,obj) {
this.control.spliceWaypoints(start,obj);
}
handleLoader() {
var { showSpinner } = this.state;
if (this.state.showSpinner === false) {
this.setState(function(prevState) {
return { showSpinner: !prevState.showSpinner };
});
return (
<Dimmer active inverted>
<Loader />
</Dimmer>
);
}
this.setState(function(prevState) {
return { showSpinner: (prevState.showSpinner = true) };
});
}
handleRemoveWayPoint() {
var waypoints = this.control.getWaypoints();
for (let i = waypoints.length - 1; i >= 0; i--) {
console.log('waypoints[i].latLng !== null ',waypoints[i].latLng !== null);
if (waypoints[i].latLng !== null) {
waypoints[i].latLng = null;
break;
}
}
console.log('waypoints ',waypoints);
this.control.setWaypoints(waypoints);
}
handleClearWayPoints() {
this.control.setWaypoints([L.latLng(null,null),L.latLng(null,null)]);
}
componentDidMount() {
const { map } = this.props.leaflet;
var { markers } = this.props;
this.control.setWaypoints([L.latLng(markers[0]),L.latLng(markers[1])]);
this.setState(prevState => {
localDispatch: prevState.localDispatch = this.context.dispatch;
});
map.addControl(this.control);
}
createLeafletElement(props) {
const { map } = this.props.leaflet;
var startIcon = new L.Icon({
iconUrl:
'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-green.png',shadowUrl:
'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',iconSize: [25,41],iconAnchor: [12,popupAnchor: [1,-34],shadowSize: [41,41]
});
var endIcon = new L.Icon({
iconUrl:
'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',41]
});
if (map && !this.control) {
var RoutingMachineRef = this;
this.control = L.Routing.control({
collapsible: true,show: false,position: 'bottomleft',lineOptions: {
styles: [{ color: 'chartreuse',opacity: 1,weight: 5 }]
},waypoints: [null],createMarker: function(i,wp,nWps) {
if (i === 0) {
return L.marker(wp.latLng,{
icon: startIcon,draggable: true,keyboard: true,alt: 'current location'
}).on('move',function(e) {
e.target._latlng.alt = 'current location';
console.log('e.target._latlng',e.target._latlng);
console.log('there be dragons start!!',e);
RoutingMachineRef.setState(prevState => ({
markerIsBeingDragged: !prevState.markerIsBeingDragged
}));
RoutingMachineRef.handleSetMarker(e.target._latlng);
});
}
if (i === nWps - 1) {
return L.marker(wp.latLng,{
icon: endIcon,alt: 'current destination'
}).on('move',function(e) {
e.target._latlng.alt = 'current destination';
console.log(' e.target._latlng',e.target._latlng);
RoutingMachineRef.setState(prevState => ({
markerIsBeingDragged: !prevState.markerIsBeingDragged
}));
console.log('there be dragons dest!!',e);
RoutingMachineRef.handleSetMarker(e.target._latlng);
});
}
}
});
L.Routing.errorControl(this.control).addTo(map);
}
return this.control.getPlan();
}
updateLeafletElement(fromProps,toProps) {
var { currentMarker,localDispatch,markerIsBeingDragged } = this.state;
// console.log('fromProps,toProps ',fromProps,toProps);
if (markerIsBeingDragged && currentMarker.hasOwnProperty('alt')) {
if (isEqual(toProps.markers[0],currentMarker) === false) {
localDispatch({
type: 'updateMarkers',payload: {
marker: currentMarker
}
});
}
if (isEqual(toProps.markers[1],payload: {
marker: currentMarker
}
});
}
this.setState(prevState => ({
markerIsBeingDragged: !prevState.markerIsBeingDragged
}));
}
if (toProps.removeRoutingMachine !== false) {
this.control.setWaypoints([]);
}
}
componentWillUnmount() {
console.log("'unmount' ",'unmount');
this.destroyRouting();
}
destroyRouting() {
const { map } = this.props.leaflet;
if (map) {
map.removeControl(this.control);
}
}
}
export default withLeaflet(Routing);