visgl / react-map-gl

React friendly API wrapper around MapboxGL JS
http://visgl.github.io/react-map-gl/
Other
7.88k stars 1.35k forks source link

Draw Polygon example TS error [Bug] #2104

Open Bartosz-L opened 1 year ago

Bartosz-L commented 1 year ago

Description

image

No overload matches this call. Overload 1 of 3, '(type: keyof MapEventType, listener: (ev: (MapboxEvent<undefined> | ErrorEvent | MapContextEvent | MapDataEvent | ... 5 more ... | MapWheelEvent) & EventData) => void): Map', gave the following error. Argument of type '"draw.create"' is not assignable to parameter of type 'keyof MapEventType'. Overload 2 of 3, '(type: string, listener: (ev: any) => void): Map', gave the following error. Argument of type '((evt: { features: object[]; }) => void) | undefined' is not assignable to parameter of type '(ev: any) => void'. Type 'undefined' is not assignable to type '(ev: any) => void'.ts(2769)

No overload matches this call. Overload 1 of 3, '(type: keyof MapEventType, listener: (ev: (MapboxEvent<undefined> | ErrorEvent | MapContextEvent | MapDataEvent | ... 5 more ... | MapWheelEvent) & EventData) => void): Map', gave the following error. Argument of type '"draw.delete"' is not assignable to parameter of type 'keyof MapEventType'. Overload 2 of 3, '(type: string, listener: (ev: any) => void): Map', gave the following error. Argument of type '((evt: { features: object[]; }) => void) | undefined' is not assignable to parameter of type '(ev: any) => void'. Type 'undefined' is not assignable to type '(ev: any) => void'.ts(2769)

No overload matches this call. Overload 1 of 3, '(type: keyof MapEventType, listener: (ev: (MapboxEvent<undefined> | ErrorEvent | MapContextEvent | MapDataEvent | ... 5 more ... | MapWheelEvent) & EventData) => void): Map', gave the following error. Argument of type '"draw.delete"' is not assignable to parameter of type 'keyof MapEventType'. Overload 2 of 3, '(type: string, listener: (ev: any) => void): Map', gave the following error. Argument of type '((evt: { features: object[]; }) => void) | undefined' is not assignable to parameter of type '(ev: any) => void'. Type 'undefined' is not assignable to type '(ev: any) => void'.ts(2769)

Example comes from here: https://github.com/visgl/react-map-gl/tree/7.0-release/examples/draw-polygon

Expected Behavior

No response

Steps to Reproduce

import MapboxDraw from '@mapbox/mapbox-gl-draw';
import type { ControlPosition, MapRef } from 'react-map-gl';
import { useControl } from 'react-map-gl';

type DrawControlProps = ConstructorParameters<typeof MapboxDraw>[0] & {
  position?: ControlPosition;

  onCreate?: (evt: { features: object[] }) => void;
  onUpdate?: (evt: { features: object[]; action: string }) => void;
  onDelete?: (evt: { features: object[] }) => void;
};

export default function DrawControl(props: DrawControlProps) {
  useControl<MapboxDraw>(
    () => new MapboxDraw(props),
    ({ map }: { map: MapRef }) => {
      map.on('draw.create', props.onCreate);
      map.on('draw.update', props.onUpdate);
      map.on('draw.delete', props.onDelete);
    },
    ({ map }: { map: MapRef }) => {
      map.off('draw.create', props.onCreate);
      map.off('draw.update', props.onUpdate);
      map.off('draw.delete', props.onDelete);
    },
    {
      position: props.position,
    },
  );

  return null;
}

DrawControl.defaultProps = {
  onCreate: () => {},
  onUpdate: () => {},
  onDelete: () => {},
};

Environment

Logs

No response

acolbourn commented 1 year ago

Here's one possible fix I'm trying that's working so far: `import { useState, useCallback } from 'react'; import MapboxDraw from '@mapbox/mapbox-gl-draw'; import { useControl } from 'react-map-gl'; import type { MapRef } from 'react-map-gl'; import { Feature } from 'geojson';

interface FeaturesById {

}

const DrawControl: React.FC = () => { const [features, setFeatures] = useState({});

console.log('features: ', features);

const onUpdate = useCallback((e: any) => { setFeatures((currFeatures) => { const newFeatures = { ...currFeatures }; for (const f of e.features) { newFeatures[f.id] = f; } return newFeatures; }); }, []);

const onDelete = useCallback((e: any) => { setFeatures((currFeatures) => { const newFeatures = { ...currFeatures }; for (const f of e.features) { delete newFeatures[f.id]; } return newFeatures; }); }, []);

useControl( () => new MapboxDraw(), ({ map }: { map: MapRef }) => { map.on('draw.create', onUpdate); map.on('draw.update', onUpdate); map.on('draw.delete', onDelete); }, ({ map }: { map: MapRef }) => { map.off('draw.create', onUpdate); map.off('draw.update', onUpdate); map.off('draw.delete', onDelete); }, { position: 'top-left', } );

return null; };

export default DrawControl;`

zerocool14pvo commented 1 year ago

@Bartosz-L


import MapboxDraw from '@mapbox/mapbox-gl-draw'
import { useControl } from 'react-map-gl'
import type { ControlPosition, MapRef } from 'react-map-gl'

type DrawControlProps = ConstructorParameters<typeof MapboxDraw>[0] & {
  position?: ControlPosition
  onCreate?: (evt: MapboxDraw.DrawCreateEvent) => void
  onUpdate?: (evt: MapboxDraw.DrawUpdateEvent) => void
  onDelete?: (evt: MapboxDraw.DrawDeleteEvent) => void
}

const DrawControl: React.FC<DrawControlProps> = (props: DrawControlProps) => {
  useControl(
    () => new MapboxDraw(props),
    ({ map }: { map: MapRef }) => {
      map.on('draw.create', e => props.onCreate && props.onCreate(e))
      map.on('draw.update', e => props.onUpdate && props.onUpdate(e))
      map.on('draw.delete', e => props.onDelete && props.onDelete(e))
    },
    ({ map }: { map: MapRef }) => {
      map.off('draw.create', e => props.onCreate && props.onCreate(e))
      map.off('draw.update', e => props.onUpdate && props.onUpdate(e))
      map.off('draw.delete', e => props.onDelete && props.onDelete(e))
    },
    {
      position: props.position,
    },
  )

  return null
}

export default DrawControl
knutmarius commented 1 year ago

Will this not create new function instances (the arrow functions), and thereby not refer to the same function in the unmount as in the mount? I would expect the on/off functions to require the same function reference in order to work as an event handler toggle?

Sidd27 commented 3 months ago

@knutmarius You can use it like this

import MapboxDraw from "@mapbox/mapbox-gl-draw";
import { useControl } from "react-map-gl";
import type { ControlPosition, MapRef } from "react-map-gl";

type DrawControlProps = ConstructorParameters<typeof MapboxDraw>[0] & {
  position?: ControlPosition;
  onCreate?: (evt: MapboxDraw.DrawCreateEvent) => void;
  onUpdate?: (evt: MapboxDraw.DrawUpdateEvent) => void;
  onDelete?: (evt: MapboxDraw.DrawDeleteEvent) => void;
};

const DrawControl: React.FC<DrawControlProps> = (props: DrawControlProps) => {
  useControl(
    // @ts-ignore
    () => new MapboxDraw(props),
    ({ map }: { map: MapRef }) => {
      if (props.onCreate) {
        map.on("draw.create", props.onCreate);
      }
      if (props.onUpdate) {
        map.on("draw.update", props.onUpdate);
      }
      if (props.onDelete) {
        map.on("draw.delete", props.onDelete);
      }
    },
    ({ map }: { map: MapRef }) => {
      if (props.onCreate) {
        map.off("draw.create", props.onCreate);
      }
      if (props.onUpdate) {
        map.off("draw.update", props.onUpdate);
      }
      if (props.onDelete) {
        map.off("draw.delete", props.onDelete);
      }
    },
    {
      position: props.position,
    }
  );

  return null;
};

export default DrawControl;