Reputation: 4436
I have the following map, as you should on the map I have a polygon and markers which are the main points of the polygon, below I have the main points of the polygon printed, i.e. the markers.
I give the user the possibility to delete points of the polygon, as you can see below in the image by clicking on the X, the point is eliminated.
The problem is this, when I click on the X, the point is eliminated as a marker, but it seems to remain as a fixed point of the polygon, which it really shouldn't do, the polygon should change its shape, based on the eliminated point.
I am not able to understand where I am wrong. Can you give me some help.
Link: codesandbox
Index:
import React from "react";
import ReactDOM from "react-dom";
import Map from "./Map";
import "./styles.css";
//import { makeStyles } from "@material-ui/core/styles";
import ExpansionPanel from "@material-ui/core/ExpansionPanel";
import ExpansionPanelSummary from "@material-ui/core/ExpansionPanelSummary";
import ExpansionPanelDetails from "@material-ui/core/ExpansionPanelDetails";
import Typography from "@material-ui/core/Typography";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ClearIcon from "@material-ui/icons/Clear";
import TextField from "@material-ui/core/TextField";
const API_KEY = "MY_API_KEY";
/*const useStyles = makeStyles(theme => ({
root: {
width: "100%"
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular
}
}));*/
const useStyles = theme => ({
root: {
width: "100%"
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular
}
});
const center = {
lat: 38.9065495,
lng: -77.0518192
};
//const classes = useStyles();
//className={classes.heading}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
paths: [
{ lat: 38.97330905858943, lng: -77.10469090410157 },
{ lat: 38.9209748864926, lng: -76.9083102888672 },
{ lat: 38.82689001319151, lng: -76.92204319902345 },
{ lat: 38.82261046915962, lng: -77.0181735701172 },
{ lat: 38.90174038629909, lng: -77.14314305253907 }
]
};
}
render() {
const { paths } = this.state;
return (
<div className="App2">
<Map
apiKey={API_KEY}
center={center}
paths={paths}
point={paths => this.setState({ paths })}
/>
{paths.map((pos, key) => {
return (
<ExpansionPanel key={key}>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<ClearIcon
style={{ color: "#dc004e" }}
onClick={() => {
paths.splice(key, 1);
console.log(paths);
this.setState({ paths: paths });
}}
/>
<Typography>Point #{key}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<TextField
fullWidth
key={"lat" + key}
label="Latitude"
type="text"
value={pos.lat}
disabled={true}
/>
<TextField
fullWidth
key={"lng" + key}
label="Longitude"
type="text"
value={pos.lng}
disabled={true}
/>
</ExpansionPanelDetails>
</ExpansionPanel>
);
})}
</div>
);
}
}
//export default withStyles(useStyles)(App);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Map:
import React, { useState, useRef, useCallback } from "react";
import {
LoadScript,
GoogleMap,
DrawingManager,
Polygon,
Marker
} from "@react-google-maps/api";
import "./styles.css";
const libraries = ["drawing"];
const options = {
drawingControl: true,
drawingControlOptions: {
drawingModes: ["polygon"]
},
polygonOptions: {
fillColor: `#2196F3`,
strokeColor: `#2196F3`,
fillOpacity: 0.5,
strokeWeight: 2,
clickable: true,
editable: true,
draggable: true,
zIndex: 1
}
};
class LoadScriptOnlyIfNeeded extends LoadScript {
componentDidMount() {
const cleaningUp = true;
const isBrowser = typeof document !== "undefined"; // require('@react-google-maps/api/src/utils/isbrowser')
const isAlreadyLoaded =
window.google &&
window.google.maps &&
document.querySelector("body.first-hit-completed"); // AJAX page loading system is adding this class the first time the app is loaded
if (!isAlreadyLoaded && isBrowser) {
// @ts-ignore
if (window.google && !cleaningUp) {
console.error("google api is already presented");
return;
}
this.isCleaningUp().then(this.injectScript);
}
if (isAlreadyLoaded) {
this.setState({ loaded: true });
}
}
}
export default function Map({ apiKey, center, paths = [], point }) {
const [path, setPath] = useState(paths);
const [state, setState] = useState({
drawingMode: "polygon"
});
const noDraw = () => {
setState(function set(prevState) {
return Object.assign({}, prevState, {
drawingMode: "maker"
});
});
};
const onPolygonComplete = React.useCallback(
function onPolygonComplete(poly) {
const polyArray = poly.getPath().getArray();
let paths = [];
polyArray.forEach(function(path) {
paths.push({ lat: path.lat(), lng: path.lng() });
});
setPath(paths);
console.log("onPolygonComplete", paths);
point(paths);
noDraw();
poly.setMap(null);
},
[point]
);
/*const onLoad = React.useCallback(function onLoad(map) {
//console.log(map);
}, []);
const onDrawingManagerLoad = React.useCallback(function onDrawingManagerLoad(
drawingManager
) {
// console.log(drawingManager);
},
[]);*/
// Define refs for Polygon instance and listeners
const polygonRef = useRef(null);
const listenersRef = useRef([]);
// Call setPath with new edited path
const onEdit = useCallback(() => {
if (polygonRef.current) {
const nextPath = polygonRef.current
.getPath()
.getArray()
.map(latLng => {
return { lat: latLng.lat(), lng: latLng.lng() };
});
setPath(nextPath);
point(nextPath);
}
}, [setPath, point]);
// Bind refs to current Polygon and listeners
const onLoad = useCallback(
polygon => {
polygonRef.current = polygon;
const path = polygon.getPath();
listenersRef.current.push(
path.addListener("set_at", onEdit),
path.addListener("insert_at", onEdit),
path.addListener("remove_at", onEdit)
);
},
[onEdit]
);
// Clean up refs
const onUnmount = useCallback(() => {
listenersRef.current.forEach(lis => lis.remove());
polygonRef.current = null;
}, []);
console.log(path);
return (
<div className="App">
<LoadScriptOnlyIfNeeded
id="script-loader"
googleMapsApiKey={apiKey}
libraries={libraries}
language="it"
region="us"
>
<GoogleMap
mapContainerClassName="App-map"
center={center}
zoom={10}
version="weekly"
//onLoad={onLoad}
>
{path.length === 0 ? (
<DrawingManager
drawingMode={state.drawingMode}
options={options}
onPolygonComplete={onPolygonComplete}
//onLoad={onDrawingManagerLoad}
editable
draggable
// Event used when manipulating and adding points
onMouseUp={onEdit}
// Event used when dragging the whole Polygon
onDragEnd={onEdit}
/>
) : (
<Polygon
options={{
fillColor: `#2196F3`,
strokeColor: `#2196F3`,
fillOpacity: 0.5,
strokeWeight: 2
}}
// Make the Polygon editable / draggable
editable
draggable
path={path}
// Event used when manipulating and adding points
onMouseUp={onEdit}
// Event used when dragging the whole Polygon
onDragEnd={onEdit}
onLoad={onLoad}
onUnmount={onUnmount}
/>
)}
{path.map((pos, key) => {
return <Marker key={key} label={"" + key} position={pos} />;
})}
</GoogleMap>
</LoadScriptOnlyIfNeeded>
</div>
);
}
Upvotes: 4
Views: 2019
Reputation: 3991
Add useEffect to Map component to update paths
for each render
export default function Map({ apiKey, center, paths = [], point }) {
const [path, setPath] = useState();
const [state, setState] = useState({
drawingMode: "polygon"
});
useEffect(() => {
setPath(paths);
}, [paths]);
.
.
.
and use filter instead of splice
In React you should never mutate the state directly.
onClick={() => {
this.setState({
paths: this.state.paths.filter((_, i) => i !== key)
});
or use splice like below
onClick={() => {
const paths = this.state.paths;
this.setState({
paths: [...paths.slice(0,key), ...paths.slice(key+1)]})
}}
Upvotes: 2
Reputation: 1390
During the Cancel Event, Call the function with the index of that array in the function parameter .
removetheArray = value => {
const { paths } = this.state;
this.setState({
paths: paths.splice(value, 1)
});
};
Function name is the removetheArray
and pass the index as the value, the array as removed and map is updated incase map is not updated you have to init the map.
Upvotes: 1