diff --git a/src/components/MapView.tsx b/src/components/MapView.tsx index 13042336f..93d3a0d24 100644 --- a/src/components/MapView.tsx +++ b/src/components/MapView.tsx @@ -1,31 +1,31 @@ +import { debounce } from 'debounce'; import React, { Component } from 'react'; import { - View, - StyleSheet, - NativeModules, - ViewProps, - NativeSyntheticEvent, - NativeMethods, HostComponent, LayoutChangeEvent, + NativeMethods, + NativeModules, + NativeSyntheticEvent, + StyleSheet, + View, + ViewProps, } from 'react-native'; -import { debounce } from 'debounce'; +import { type Location } from '../modules/location/locationManager'; +import NativeMapViewModule from '../specs/NativeMapViewModule'; import NativeMapView, { type NativeMapViewActual, } from '../specs/RNMBXMapViewNativeComponent'; -import NativeMapViewModule from '../specs/NativeMapViewModule'; +import { type Position } from '../types/Position'; import { - isFunction, isAndroid, + isFunction, type NativeArg, type OrnamentPositonProp, } from '../utils'; import { getFilter } from '../utils/filterUtils'; import Logger from '../utils/Logger'; import { FilterExpression } from '../utils/MapboxStyles'; -import { type Position } from '../types/Position'; -import { type Location } from '../modules/location/locationManager'; import NativeBridgeComponent from './NativeBridgeComponent'; @@ -158,7 +158,7 @@ type LocalizeLabels = } | true; -type Props = ViewProps & { +export type MapViewProps = ViewProps & { /** * The distance from the edges of the map view’s frame to the edges of the map view’s logical viewport. * @deprecated use Camera `padding` instead @@ -474,10 +474,10 @@ type Debounced = F & { clear(): void; flush(): void }; * MapView backed by Mapbox Native GL */ class MapView extends NativeBridgeComponent( - React.PureComponent, + React.PureComponent, NativeMapViewModule, ) { - static defaultProps: Props = { + static defaultProps: MapViewProps = { scrollEnabled: true, pitchEnabled: true, rotateEnabled: true, @@ -527,7 +527,7 @@ class MapView extends NativeBridgeComponent( isUserInteraction: boolean; }; - constructor(props: Props) { + constructor(props: MapViewProps) { super(props); this.logger = Logger.sharedInstance(); @@ -570,11 +570,11 @@ class MapView extends NativeBridgeComponent( this.logger.stop(); } - UNSAFE_componentWillReceiveProps(nextProps: Props) { + UNSAFE_componentWillReceiveProps(nextProps: MapViewProps) { this._setHandledMapChangedEvents(nextProps); } - _setHandledMapChangedEvents(props: Props) { + _setHandledMapChangedEvents(props: MapViewProps) { if (isAndroid() || RNMBXModule.MapboxV10) { const events: string[] = []; @@ -1110,7 +1110,7 @@ class MapView extends NativeBridgeComponent( } } - _setStyleURL(props: Props) { + _setStyleURL(props: MapViewProps) { // user set a styleURL, no need to alter props if (props.styleURL) { return; @@ -1128,7 +1128,7 @@ class MapView extends NativeBridgeComponent( } } - _setLocalizeLabels(props: Props) { + _setLocalizeLabels(props: MapViewProps) { if (!RNMBXModule.MapboxV10) { return; } @@ -1184,7 +1184,7 @@ class MapView extends NativeBridgeComponent( } type NativeProps = Omit< - Props, + MapViewProps, 'onPress' | 'onLongPress' | 'onCameraChanged' > & { onPress?: ( diff --git a/src/components/MarkerView.tsx b/src/components/MarkerView.tsx index 91fc8876a..e2f213a1d 100644 --- a/src/components/MarkerView.tsx +++ b/src/components/MarkerView.tsx @@ -11,7 +11,7 @@ import PointAnnotation from './PointAnnotation'; const Mapbox = NativeModules.RNMBXModule; -type Props = ViewProps & { +export type MarkerViewProps = ViewProps & { /** * The center point (specified as a map coordinate) of the marker. */ @@ -61,8 +61,8 @@ type Props = ViewProps & { * This component has no dedicated `onPress` method. Instead, you should handle gestures * with the React views passed in as `children`. */ -class MarkerView extends React.PureComponent { - static defaultProps: Partial = { +class MarkerView extends React.PureComponent { + static defaultProps: Partial = { anchor: { x: 0.5, y: 0.5 }, allowOverlap: false, allowOverlapWithPuck: false, diff --git a/src/web/components/MapView.tsx b/src/web/components/MapView.tsx index 77ca12889..e3b70155f 100644 --- a/src/web/components/MapView.tsx +++ b/src/web/components/MapView.tsx @@ -1,51 +1,66 @@ -import React from 'react'; import mapboxgl from 'mapbox-gl'; +import React, { useEffect, useRef, useState } from 'react'; +import { point } from '@turf/helpers'; +import { MapViewProps } from '../../components/MapView'; import MapContext from '../MapContext'; /** * MapView backed by Mapbox GL KS */ -class MapView extends React.Component< - { styleURL: string; children: JSX.Element }, - { map?: object | null } -> { - state = { map: null }; - mapContainer: HTMLElement | null = null; - map: object | null = null; - - componentDidMount() { - const { styleURL } = this.props; - if (!this.mapContainer) { - console.error('MapView - mapContainer should is null'); +export default function MapView( + props: Pick, +) { + const mapContainerRef = useRef(null); + + const [map, setMap] = useState(undefined); + + const _propsRef = useRef(props); + useEffect(() => { + _propsRef.current = props; + }, [props]); + + useEffect(() => { + if (mapContainerRef.current === null) { + console.error('MapView - mapContainerRef should not be null'); return; } - const map = new mapboxgl.Map({ - container: this.mapContainer, + + // Initialize map + const { styleURL } = props; + const _map = new mapboxgl.Map({ + container: mapContainerRef.current, style: styleURL || 'mapbox://styles/mapbox/streets-v11', }); - this.map = map; - this.setState({ map }); - } - - render() { - const { children } = this.props; - const { map } = this.state; - return ( -
(this.mapContainer = el)} - > - {map && ( -
- - {children} - -
- )} -
- ); - } -} -export default MapView; + // Set map event listeners + _map.on('click', (e) => { + if (_propsRef.current.onPress === undefined) { + return; + } + + _propsRef.current.onPress(point(e.lngLat.toArray())); + }); + + setMap(_map); + + return () => { + _map.remove(); + if (_map === map) { + setMap(undefined); + } + }; + }, []); + + return ( +
+ {map && ( +
+ + {props.children} + +
+ )} +
+ ); +} diff --git a/src/web/components/MarkerView.tsx b/src/web/components/MarkerView.tsx index 4cfa4a457..efb16b335 100644 --- a/src/web/components/MarkerView.tsx +++ b/src/web/components/MarkerView.tsx @@ -3,7 +3,6 @@ import { forwardRef, isValidElement, memo, - ReactElement, Ref, useContext, useEffect, @@ -12,14 +11,13 @@ import { } from 'react'; import { createPortal } from 'react-dom'; +import { MarkerViewProps } from '../../components/MarkerView'; import MapContext from '../MapContext'; -type MarkerViewProps = { - coordinate: [number, number]; - children?: ReactElement; -}; - -function MarkerView(props: MarkerViewProps, ref: Ref) { +function MarkerView( + props: Pick, + ref: Ref, +) { const { map } = useContext(MapContext); // Create marker instance @@ -30,8 +28,12 @@ function MarkerView(props: MarkerViewProps, ref: Ref) { : undefined, }); - // Set marker coordinates - _marker.setLngLat(props.coordinate); + if (props.coordinate.length !== 2) { + console.error('MarkerView - coordinate should be an array of length 2'); + } else { + // Set marker coordinates + _marker.setLngLat(props.coordinate as [number, number]); + } // Fix marker position const { style } = _marker.getElement();