Closed abewartech closed 3 years ago
I have the same problem! any solution?
Hello, are you sure you have installed react-leaflet and leaflet-draw ?
npm install leaflet-draw react-leaflet
Yes, of course
I was only able to use it by downgrading "react-leaflet" to 2.7.0 Here is my used packages. Hope it helps.
"react-leaflet": "^2.7.0", "react-leaflet-draw": "^0.19.0", "leaflet": "^1.7.1", "leaflet-draw": "^1.0.4"
I took a stab at refactoring this; I only dumped about a half hour into it/copied the most basic pattern from the docs, so it's ugly and doesn't use their cleaner, custom hooks but it works for my use case. Didn't rigorously test all the eventHandlers either. Please reply with improvements if you iterate on it
import 'leaflet-draw/dist/leaflet.draw.css' // wherever you need it
// Refactored v3 EditControl.js file
import { useEffect, useRef } from 'react';
import { PropTypes } from 'prop-types';
import Draw from 'leaflet-draw'; // eslint-disable-line
import isEqual from 'lodash.isequal';
import { useLeafletContext } from '@react-leaflet/core';
import leaflet, { Map, Control } from 'leaflet';
const eventHandlers = {
onEdited: 'draw:edited',
onDrawStart: 'draw:drawstart',
onDrawStop: 'draw:drawstop',
onDrawVertex: 'draw:drawvertex',
onEditStart: 'draw:editstart',
onEditMove: 'draw:editmove',
onEditResize: 'draw:editresize',
onEditVertex: 'draw:editvertex',
onEditStop: 'draw:editstop',
onDeleted: 'draw:deleted',
onDeleteStart: 'draw:deletestart',
onDeleteStop: 'draw:deletestop',
};
function EditControl(props) {
const context = useLeafletContext()
const controlRef = useRef()
const propsRef = useRef(props)
const onDrawCreate = (e) => {
context.layerContainer.addLayer(e.layer);
props.onCreated && props.onCreated(e);
};
useEffect(() => {
for (const key in eventHandlers) {
context.map.on(eventHandlers[key], (evt) => {
let handlers = Object.keys(eventHandlers).filter(handler => eventHandlers[handler] === evt.type);
if (handlers.length === 1) {
let handler = handlers[0];
props[handler] && props[handler](evt);
}
})
}
context.map.on(leaflet.Draw.Event.CREATED, onDrawCreate);
const options = {
edit: {
...props.edit,
featureGroup: context.layerContainer
}
}
if (props.draw) {
options.draw = { ...props.draw };
}
if (props.position) {
options.position = props.position;
}
controlRef.current = new Control.Draw(options);
controlRef.current.addTo(context.map);
props.onMounted && props.onMounted(controlRef.current);
return () => {
context.map.off(leaflet.Draw.Event.CREATED, onDrawCreate);
for (const key in eventHandlers) {
if (props[key]) {
context.map.off(eventHandlers[key], props[key]);
}
}
}
}, [])
useEffect(() => {
// If the props haven't changed, don't update
if (
isEqual(props.draw, propsRef.current.draw)
&& isEqual(props.edit, propsRef.current.edit)
&& props.position === propsRef.current.position
) {
return false;
}
const options = {
edit: {
...props.edit,
featureGroup: context.layerContainer
}
}
if (props.draw) {
options.draw = { ...props.draw };
}
if (props.position) {
options.position = props.position;
}
controlRef.current.remove(context.map);
controlRef.current = new Control.Draw(options);
controlRef.current.addTo(context.map);
// Remount the new draw control
props.onMounted && props.onMounted(controlRef.current);
propsRef.current = props
}, [props.draw, props.edit, props.position])
return null;
}
EditControl.propTypes = {
...Object.keys(eventHandlers).reduce((acc, val) => {
acc[val] = PropTypes.func;
return acc;
}, {}),
onCreated: PropTypes.func,
onMounted: PropTypes.func,
draw: PropTypes.shape({
polyline: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
polygon: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
rectangle: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
circle: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
marker: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
}),
edit: PropTypes.shape({
edit: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
remove: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
poly: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
allowIntersection: PropTypes.bool,
}),
position: PropTypes.oneOf([
'topright',
'topleft',
'bottomright',
'bottomleft'
]),
};
export default EditControl;
I got this error
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
I took a stab at refactoring this; I only dumped about a half hour into it/copied the most basic pattern from the docs, so it's ugly and doesn't use their cleaner, custom hooks but it works for my use case. Didn't rigorously test all the eventHandlers either. Please reply with improvements if you iterate on it
import 'leaflet-draw/dist/leaflet.draw.css' // wherever you need it
// Refactored v3 EditControl.js file import { useEffect, useRef } from 'react'; import { PropTypes } from 'prop-types'; import Draw from 'leaflet-draw'; // eslint-disable-line import isEqual from 'lodash.isequal'; import { useLeafletContext } from '@react-leaflet/core'; import leaflet, { Map, Control } from 'leaflet'; const eventHandlers = { onEdited: 'draw:edited', onDrawStart: 'draw:drawstart', onDrawStop: 'draw:drawstop', onDrawVertex: 'draw:drawvertex', onEditStart: 'draw:editstart', onEditMove: 'draw:editmove', onEditResize: 'draw:editresize', onEditVertex: 'draw:editvertex', onEditStop: 'draw:editstop', onDeleted: 'draw:deleted', onDeleteStart: 'draw:deletestart', onDeleteStop: 'draw:deletestop', }; function EditControl(props) { const context = useLeafletContext() const controlRef = useRef() const propsRef = useRef(props) const onDrawCreate = (e) => { context.layerContainer.addLayer(e.layer); props.onCreated && props.onCreated(e); }; useEffect(() => { for (const key in eventHandlers) { context.map.on(eventHandlers[key], (evt) => { let handlers = Object.keys(eventHandlers).filter(handler => eventHandlers[handler] === evt.type); if (handlers.length === 1) { let handler = handlers[0]; props[handler] && props[handler](evt); } }) } context.map.on(leaflet.Draw.Event.CREATED, onDrawCreate); const options = { edit: { ...props.edit, featureGroup: context.layerContainer } } if (props.draw) { options.draw = { ...props.draw }; } if (props.position) { options.position = props.position; } controlRef.current = new Control.Draw(options); controlRef.current.addTo(context.map); props.onMounted && props.onMounted(controlRef.current); return () => { context.map.off(leaflet.Draw.Event.CREATED, onDrawCreate); for (const key in eventHandlers) { if (props[key]) { context.map.off(eventHandlers[key], props[key]); } } } }, []) useEffect(() => { // If the props haven't changed, don't update if ( isEqual(props.draw, propsRef.current.draw) && isEqual(props.edit, propsRef.current.edit) && props.position === propsRef.current.position ) { return false; } const options = { edit: { ...props.edit, featureGroup: context.layerContainer } } if (props.draw) { options.draw = { ...props.draw }; } if (props.position) { options.position = props.position; } controlRef.current.remove(context.map); controlRef.current = new Control.Draw(options); controlRef.current.addTo(context.map); // Remount the new draw control props.onMounted && props.onMounted(controlRef.current); propsRef.current = props }, [props.draw, props.edit, props.position]) return null; } EditControl.propTypes = { ...Object.keys(eventHandlers).reduce((acc, val) => { acc[val] = PropTypes.func; return acc; }, {}), onCreated: PropTypes.func, onMounted: PropTypes.func, draw: PropTypes.shape({ polyline: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), polygon: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), rectangle: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), circle: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), marker: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), }), edit: PropTypes.shape({ edit: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), remove: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), poly: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), allowIntersection: PropTypes.bool, }), position: PropTypes.oneOf([ 'topright', 'topleft', 'bottomright', 'bottomleft' ]), }; export default EditControl;
when i link to another route , i'll get an error like this this error is occured in EditControl like this @r2dliu i don't how to fix this error
WARNING in ./node_modules/react-leaflet-draw/dist/esm/EditControl.js 131:2-12 "export 'MapControl' was not found in 'react-leaflet'
WARNING in ./node_modules/react-leaflet-draw/dist/esm/EditControl.js 184:15-26 "export 'withLeaflet' was not found in 'react-leaflet'
@srghma react-leaflet upgrade to v3, MapControl and withLeaflet is not fond in old version
@chaosJS I'm not working directly on react-leaflet-draw, but I can accept pr
I confirm that react-leaflet-draw 0.19.0 is incompatible with react-leaflet 3.0.0 and higher. I've got the same error: 'MapControl' is not exported from 'react-leaflet' which goes away only if I downgrade react-leaflet to version 2.7.0
I took a stab at refactoring this; I only dumped about a half hour into it/copied the most basic pattern from the docs, so it's ugly and doesn't use their cleaner, custom hooks but it works for my use case. Didn't rigorously test all the eventHandlers either. Please reply with improvements if you iterate on it
import 'leaflet-draw/dist/leaflet.draw.css' // wherever you need it
// Refactored v3 EditControl.js file import { useEffect, useRef } from 'react'; import { PropTypes } from 'prop-types'; import Draw from 'leaflet-draw'; // eslint-disable-line import isEqual from 'lodash.isequal'; import { useLeafletContext } from '@react-leaflet/core'; import leaflet, { Map, Control } from 'leaflet'; const eventHandlers = { onEdited: 'draw:edited', onDrawStart: 'draw:drawstart', onDrawStop: 'draw:drawstop', onDrawVertex: 'draw:drawvertex', onEditStart: 'draw:editstart', onEditMove: 'draw:editmove', onEditResize: 'draw:editresize', onEditVertex: 'draw:editvertex', onEditStop: 'draw:editstop', onDeleted: 'draw:deleted', onDeleteStart: 'draw:deletestart', onDeleteStop: 'draw:deletestop', }; function EditControl(props) { const context = useLeafletContext() const controlRef = useRef() const propsRef = useRef(props) const onDrawCreate = (e) => { context.layerContainer.addLayer(e.layer); props.onCreated && props.onCreated(e); }; useEffect(() => { for (const key in eventHandlers) { context.map.on(eventHandlers[key], (evt) => { let handlers = Object.keys(eventHandlers).filter(handler => eventHandlers[handler] === evt.type); if (handlers.length === 1) { let handler = handlers[0]; props[handler] && props[handler](evt); } }) } context.map.on(leaflet.Draw.Event.CREATED, onDrawCreate); const options = { edit: { ...props.edit, featureGroup: context.layerContainer } } if (props.draw) { options.draw = { ...props.draw }; } if (props.position) { options.position = props.position; } controlRef.current = new Control.Draw(options); controlRef.current.addTo(context.map); props.onMounted && props.onMounted(controlRef.current); return () => { context.map.off(leaflet.Draw.Event.CREATED, onDrawCreate); for (const key in eventHandlers) { if (props[key]) { context.map.off(eventHandlers[key], props[key]); } } } }, []) useEffect(() => { // If the props haven't changed, don't update if ( isEqual(props.draw, propsRef.current.draw) && isEqual(props.edit, propsRef.current.edit) && props.position === propsRef.current.position ) { return false; } const options = { edit: { ...props.edit, featureGroup: context.layerContainer } } if (props.draw) { options.draw = { ...props.draw }; } if (props.position) { options.position = props.position; } controlRef.current.remove(context.map); controlRef.current = new Control.Draw(options); controlRef.current.addTo(context.map); // Remount the new draw control props.onMounted && props.onMounted(controlRef.current); propsRef.current = props }, [props.draw, props.edit, props.position]) return null; } EditControl.propTypes = { ...Object.keys(eventHandlers).reduce((acc, val) => { acc[val] = PropTypes.func; return acc; }, {}), onCreated: PropTypes.func, onMounted: PropTypes.func, draw: PropTypes.shape({ polyline: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), polygon: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), rectangle: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), circle: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), marker: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), }), edit: PropTypes.shape({ edit: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), remove: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), poly: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), allowIntersection: PropTypes.bool, }), position: PropTypes.oneOf([ 'topright', 'topleft', 'bottomright', 'bottomleft' ]), }; export default EditControl;
when i link to another route , i'll get an error like this this error is occured in EditControl like this @r2dliu i don't how to fix this error
Does this fix the issue? If so, would you please make a PR?
@chaosJS I'm not working directly on react-leaflet-draw, but I can accept pr
I can try this, but I don't know how to get started. I am new. So I have forked you repo, clone and everything. How can I run your code after making changes to src/EditControl.js
@srghma Please check my pr and merge.
@abewartech @ehsan-sotoodeh @MarcoGiorgi @bokurev @r2dliu @L4brax @ Guys tried to update the library, waiting for the comments from the collaborator.
Hello guys, thanks for the PR @DVGY , are you going to release it as a new version?
@cargaralo I don't have rights to release to non, only @alex3165 have
Hello guys, thanks for the PR @DVGY , are you going to release it as a new version?
@cargaralo Thanks! It Can only released by the owner. For the time being you can define your own cutom file EditControl.js
and copy the code from #93 and use it normally.
Just to give a different possibility, we solved this the way react-leaflet probably intended. Since we had to extract the featureGroup
from the context, we modified createControlComponent
slightly; apart from that, it should be exactly the same in the original code. I mention this because while it looks daunting it was literally 2 function calls away.
Anyway, here's our code:
import { useCallback, useEffect, useRef } from 'react';
import leaflet, { Control } from 'leaflet';
import Draw from 'leaflet-draw';
import {
useLeafletContext,
createElementHook,
createLeafComponent,
} from '@react-leaflet/core';
const createControlComponent = (createInstance) => {
function createElement(props, context) {
const { layerContainer } = context;
const { position } = props;
const options = {
position,
edit: {
featureGroup: layerContainer,
},
};
return { instance: createInstance(options), context };
}
const useElement = createElementHook(createElement);
const useControl = createControlHook(useElement);
return createLeafComponent(useControl);
};
const createControlHook = (useElement) => {
return function useLeafletControl(props) {
const context = useLeafletContext();
const elementRef = useElement(props, context);
const { instance } = elementRef.current;
const positionRef = useRef(props.position);
const { position, onCreated, onEdit, onDeleted } = props;
const onDrawCreate = useCallback(
(e) => {
context.layerContainer.addLayer(e.layer);
onCreated(e);
},
[context.layerContainer, onCreated]
);
useEffect(
function addControl() {
instance.addTo(context.map);
context.map.on(leaflet.Draw.Event.CREATED, onDrawCreate);
if (onDeleted) {
context.map.on(leaflet.Draw.Event.DELETED, onDeleted);
}
if (onEdit) {
context.map.on(leaflet.Draw.Event.EDITRESIZE, onEdit);
context.map.on(leaflet.Draw.Event.EDITMOVE, onEdit);
}
return function removeControl() {
context.map.off(leaflet.Draw.Event.CREATED, onDrawCreate);
if (onDeleted) {
context.map.off(leaflet.Draw.Event.DELETED, onDeleted);
}
if (onEdit) {
context.map.off(leaflet.Draw.Event.EDITRESIZE, onEdit);
context.map.off(leaflet.Draw.Event.EDITMOVE, onEdit);
}
instance.remove();
};
},
[context.map, instance, onDrawCreate, onDeleted, onEdit]
);
useEffect(
function updateControl() {
if (position != null && position !== positionRef.current) {
instance.setPosition(position);
positionRef.current = position;
}
},
[instance, position]
);
return elementRef;
};
};
Then, to add a custom control:
export const EditOnlyControl = createControlComponent(
(options) =>
new Control.Draw({
...options,
draw: {
polyline: false,
polygon: false,
rectangle: false,
circle: false,
marker: false,
circlemarker: false,
},
})
);
Of course, it still needs to be used inside a <FeatureGroup>
This issue was solved by #90, right? Any ideas on when the new version will be released? Anyone know? @alex3165
Really great work with this package. So much appreciation from all of us.
@davis3tnpolitics, Alex is not working on project anymore, I have rights to github repo, but not for npm, do the new version won't be released, but it is merged, yes, I would suggest to install from github master itself if this is possible
Thank you!!
@srghma peerDependencies
still has "react-leaflet": "^2.0.0"
, which fails to install:
npm ERR! Could not resolve dependency:
npm ERR! peer react-leaflet@"^2.0.0" from react-leaflet-draw@0.19.8
npm ERR! node_modules/react-leaflet-draw
npm ERR! react-leaflet-draw@"https://github.com/alex3165/react-leaflet-draw" from the root project
Can you fix this? I can work around it with --force
, but that is not ideal.
WARNING in ./node_modules/react-leaflet-draw/dist/esm/EditControl.js 131:2-12 "export 'MapControl' was not found in 'react-leaflet'
WARNING in ./node_modules/react-leaflet-draw/dist/esm/EditControl.js 184:15-26 "export 'withLeaflet' was not found in 'react-leaflet'