Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React-Leaflet v3 #87

Closed
abewartech opened this issue Nov 2, 2020 · 21 comments
Closed

React-Leaflet v3 #87

abewartech opened this issue Nov 2, 2020 · 21 comments

Comments

@abewartech
Copy link

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'

@ehsan-sotoodeh
Copy link

I have the same problem! any solution?

@L4brax
Copy link

L4brax commented Nov 26, 2020

Hello, are you sure you have installed react-leaflet and leaflet-draw ?
npm install leaflet-draw react-leaflet

@abewartech
Copy link
Author

image
Yes, of course

@tadassuksteris
Copy link

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"

@r2dliu
Copy link

r2dliu commented Dec 9, 2020

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;

@ghost
Copy link

ghost commented Dec 10, 2020

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:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. 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.

@chaosJS
Copy link

chaosJS commented Dec 17, 2020

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
image
this error is occured in EditControl like this
image
@r2dliu i don't how to fix this error

@chaosJS
Copy link

chaosJS commented Dec 17, 2020

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

@srghma
Copy link
Collaborator

srghma commented Dec 17, 2020

@chaosJS I'm not working directly on react-leaflet-draw, but I can accept pr

@MarcoGiorgi
Copy link

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

@dartmoordunbar
Copy link

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
image
this error is occured in EditControl like this
image
@r2dliu i don't how to fix this error

Does this fix the issue? If so, would you please make a PR?

@DVGY
Copy link
Contributor

DVGY commented Jan 31, 2021

@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

@DVGY
Copy link
Contributor

DVGY commented Feb 5, 2021

@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.

#90

@cargaralo
Copy link

Hello guys, thanks for the PR @DVGY , are you going to release it as a new version?

@srghma
Copy link
Collaborator

srghma commented Feb 11, 2021

@cargaralo I don't have rights to release to non, only @alex3165 have

@DVGY
Copy link
Contributor

DVGY commented Feb 15, 2021

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.

@pollomarzo
Copy link

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>

@davis3tnpolitics
Copy link

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.

@srghma
Copy link
Collaborator

srghma commented Mar 24, 2021

@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

@gmarshall56
Copy link

Thank you!!

@paulschreiber
Copy link

paulschreiber commented May 3, 2022

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests