diff --git a/docs/advanced/custom-components.md b/docs/advanced/custom-components.md index a5b27886..aeb5a6e2 100644 --- a/docs/advanced/custom-components.md +++ b/docs/advanced/custom-components.md @@ -27,7 +27,7 @@ class CustomMarker extends BaseControl { }; return ( -
({longitude}, {latitude})
@@ -52,20 +52,29 @@ Stop propagation of click event to the map component. Can be used to stop map fr ##### `captureDoubleClick` {Boolean} - default: `true` Stop propagation of dblclick event to the map component. Can be used to stop map from zooming when this component is double clicked. -## Private Methods +## Private Members -##### `_render` +##### `_containerRef` -Implement this method to render the content of this component. `this._context` is accessible when this method is called, containing the following fields: +A React [ref](https://reactjs.org/docs/refs-and-the-dom.html#creating-refs) object. + +Should be assigned to the `ref` prop of the root DOM element of this component. Required to leverage the `capture*` props. + +##### `_context` + +An object containing the following fields: - `viewport` {WebMercatorViewport} - the current viewport - `map` {mapboxgl.Map} - the Mapbox map instance - `eventManager` {EventManager} - the event manager. Only available if using `InteractiveMap`. - `isDragging` {Bool} - whether the map is being dragged. Only available if using `InteractiveMap`. -##### `_onContainerLoad` -Should be assigned to the `ref` prop of the root DOM element of this component. This is required to leverage the `capture*` props. +## Private Methods + +##### `_render` + +Implement this method to render the content of this component. `this._context` is accessible when this method is called. ## Source diff --git a/src/components/base-control.js b/src/components/base-control.js index 459f7f7e..720c29f9 100644 --- a/src/components/base-control.js +++ b/src/components/base-control.js @@ -17,7 +17,7 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import {PureComponent, createElement} from 'react'; +import {PureComponent, createElement, createRef} from 'react'; import PropTypes from 'prop-types'; import {InteractiveContext} from './interactive-map'; import {StaticContext} from './static-map'; @@ -54,7 +54,27 @@ export default class BaseControl extends PureComponent { this._context = {}; this._events = null; - this._containerRef = null; + this._containerRef = createRef(); + } + + componentDidMount() { + const ref = this._containerRef.current; + if (!ref) { + return; + } + + const {eventManager} = this._context; + + // Return early if no eventManager is found + if (eventManager) { + this._events = { + wheel: this._onScroll, + panstart: this._onDragStart, + click: this._onClick, + pointerup: this._onPointerUp + }; + eventManager.on(this._events, ref); + } } componentWillUnmount() { @@ -64,39 +84,6 @@ export default class BaseControl extends PureComponent { } } - _onContainerLoad = (ref) => { - this._containerRef = ref; - - const {eventManager} = this._context; - - // Return early if no eventManager is found - if (!eventManager) { - return; - } - - let events = this._events; - - // Remove all previously registered events - if (events) { - eventManager.off(events); - events = null; - } - - if (ref) { - // container is mounted: register events for this element - events = { - wheel: this._onScroll, - panstart: this._onDragStart, - click: this._onClick, - pointerup: this._onPointerUp - }; - - eventManager.on(events, ref); - } - - this._events = events; - } - _onScroll = (evt) => { if (this.props.captureScroll) { evt.stopPropagation(); diff --git a/src/components/draggable-control.js b/src/components/draggable-control.js index aa855c29..7e66c773 100644 --- a/src/components/draggable-control.js +++ b/src/components/draggable-control.js @@ -81,7 +81,7 @@ export default class DraggableControl extends BaseControl { */ _getDragEventOffset(event) { const {center: {x, y}} = event; - const rect = this._containerRef.getBoundingClientRect(); + const rect = this._containerRef.current.getBoundingClientRect(); return [rect.left - x, rect.top - y]; } diff --git a/src/components/interactive-map.js b/src/components/interactive-map.js index 08580c65..12309c70 100644 --- a/src/components/interactive-map.js +++ b/src/components/interactive-map.js @@ -1,4 +1,4 @@ -import {PureComponent, createElement, createContext} from 'react'; +import {PureComponent, createElement, createContext, createRef} from 'react'; import PropTypes from 'prop-types'; import StaticMap from './static-map'; @@ -155,11 +155,14 @@ export default class InteractiveMap extends PureComponent { }); this._width = 0; this._height = 0; + this._eventCanvasRef = createRef(); + this._staticMapRef = createRef(); } componentDidMount() { const eventManager = this._eventManager; + eventManager.setElement(this._eventCanvasRef.current); // Register additional event handlers for click and hover eventManager.on({ pointerdown: this._onPointerDown, @@ -180,7 +183,7 @@ export default class InteractiveMap extends PureComponent { } getMap = () => { - return this._map ? this._map.getMap() : null; + return this._staticMapRef.current ? this._staticMapRef.current.getMap() : null; } queryRenderedFeatures = (geometry, options) => { @@ -350,15 +353,6 @@ export default class InteractiveMap extends PureComponent { } } - _eventCanvasLoaded = (ref) => { - // This will be called with `null` after unmount, releasing event manager resource - this._eventManager.setElement(ref); - } - - _staticMapLoaded = (ref) => { - this._map = ref; - } - render() { const {width, height, style, getCursor} = this.props; @@ -375,7 +369,7 @@ export default class InteractiveMap extends PureComponent { return createElement(InteractiveContext.Provider, {value: interactiveContext}, createElement('div', { key: 'map-controls', - ref: this._eventCanvasLoaded, + ref: this._eventCanvasRef, style: eventCanvasStyle }, createElement(StaticMap, Object.assign({}, this.props, @@ -384,7 +378,7 @@ export default class InteractiveMap extends PureComponent { height: '100%', style: null, onResize: this._onResize, - ref: this._staticMapLoaded, + ref: this._staticMapRef, children: this.props.children } )) diff --git a/src/components/marker.js b/src/components/marker.js index acbd5589..a5c3b32a 100644 --- a/src/components/marker.js +++ b/src/components/marker.js @@ -69,7 +69,7 @@ export default class Marker extends DraggableControl { return createElement('div', { className: `mapboxgl-marker ${className}`, - ref: this._onContainerLoad, + ref: this._containerRef, style: containerStyle, children: this.props.children }); diff --git a/src/components/navigation-control.js b/src/components/navigation-control.js index 72e13e1f..5c9e7c8f 100644 --- a/src/components/navigation-control.js +++ b/src/components/navigation-control.js @@ -94,7 +94,7 @@ export default class NavigationControl extends BaseControl { return createElement('div', { className: `mapboxgl-ctrl mapboxgl-ctrl-group ${className}`, - ref: this._onContainerLoad + ref: this._containerRef }, [ showZoom && this._renderButton('zoom-in', 'Zoom In', this._onZoomIn), showZoom && this._renderButton('zoom-out', 'Zoom Out', this._onZoomOut), diff --git a/src/components/popup.js b/src/components/popup.js index 050eea13..513f5115 100644 --- a/src/components/popup.js +++ b/src/components/popup.js @@ -17,7 +17,7 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import {createElement} from 'react'; +import {createElement, createRef} from 'react'; import PropTypes from 'prop-types'; import BaseControl from './base-control'; @@ -79,9 +79,11 @@ export default class Popup extends BaseControl { super(props); this._closeOnClick = false; + this._contentRef = createRef(); } componentDidMount() { + super.componentDidMount(); // Container just got a size, re-calculate position this.forceUpdate(); } @@ -89,15 +91,16 @@ export default class Popup extends BaseControl { _getPosition(x, y) { const {viewport} = this._context; const {anchor, dynamicPosition, tipSize} = this.props; + const content = this._contentRef.current; - if (this._content) { + if (content) { return dynamicPosition ? getDynamicPosition({ x, y, anchor, padding: tipSize, width: viewport.width, height: viewport.height, - selfWidth: this._content.clientWidth, - selfHeight: this._content.clientHeight + selfWidth: content.clientWidth, + selfHeight: content.clientHeight }) : anchor; } @@ -153,10 +156,6 @@ export default class Popup extends BaseControl { this._closeOnClick = true; } - _contentLoaded = (ref) => { - this._content = ref; - } - _renderTip(positionType) { const {tipSize} = this.props; @@ -171,7 +170,7 @@ export default class Popup extends BaseControl { const {closeButton, children} = this.props; return createElement('div', { key: 'content', - ref: this._contentLoaded, + ref: this._contentRef, className: 'mapboxgl-popup-content' }, [ closeButton && createElement('button', { @@ -195,7 +194,7 @@ export default class Popup extends BaseControl { return createElement('div', { className: `mapboxgl-popup mapboxgl-popup-anchor-${positionType} ${className}`, style: containerStyle, - ref: this._onContainerLoad + ref: this._containerRef }, [ this._renderTip(positionType), this._renderContent() diff --git a/src/components/static-map.js b/src/components/static-map.js index 4a49f861..8d5484f0 100644 --- a/src/components/static-map.js +++ b/src/components/static-map.js @@ -17,7 +17,7 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -import {PureComponent, createElement, createContext} from 'react'; +import {PureComponent, createElement, createContext, createRef} from 'react'; import PropTypes from 'prop-types'; import {normalizeStyle} from '../utils/style-utils'; @@ -102,6 +102,7 @@ export default class StaticMap extends PureComponent { }; this._width = 0; this._height = 0; + this._mapboxMapRef = createRef(); } componentDidMount() { @@ -111,7 +112,7 @@ export default class StaticMap extends PureComponent { mapboxgl, // Handle to mapbox-gl library width: this._width, height: this._height, - container: this._mapboxMap, + container: this._mapboxMapRef.current, onError: this._mapboxMapError, mapStyle: normalizeStyle(mapStyle) })); @@ -175,10 +176,6 @@ export default class StaticMap extends PureComponent { })); } - _mapboxMapLoaded = (ref) => { - this._mapboxMap = ref; - } - // Handle map error _mapboxMapError = (evt) => { const statusCode = evt.error && evt.error.status || evt.status; @@ -250,7 +247,7 @@ export default class StaticMap extends PureComponent { children: [ createElement('div', { key: 'map-mapbox', - ref: this._mapboxMapLoaded, + ref: this._mapboxMapRef, style: mapStyle, className }), diff --git a/src/overlays/canvas-overlay.js b/src/overlays/canvas-overlay.js index 66a466b2..e8b28ef2 100644 --- a/src/overlays/canvas-overlay.js +++ b/src/overlays/canvas-overlay.js @@ -40,6 +40,8 @@ export default class CanvasOverlay extends BaseControl { } componentDidMount() { + this._canvas = this._containerRef.current; + this._ctx = this._canvas.getContext('2d'); this._redraw(); } @@ -66,12 +68,6 @@ export default class CanvasOverlay extends BaseControl { ctx.restore(); } - _canvasLoaded = (ref) => { - this._canvas = ref; - this._ctx = ref && ref.getContext('2d'); - this._onContainerLoad(ref); - } - _render() { const pixelRatio = window.devicePixelRatio || 1; const {viewport: {width, height}} = this._context; @@ -79,7 +75,7 @@ export default class CanvasOverlay extends BaseControl { return ( createElement('canvas', { - ref: this._canvasLoaded, + ref: this._containerRef, width: width * pixelRatio, height: height * pixelRatio, style: { diff --git a/src/overlays/html-overlay.js b/src/overlays/html-overlay.js index 668622aa..9a827cf9 100644 --- a/src/overlays/html-overlay.js +++ b/src/overlays/html-overlay.js @@ -47,7 +47,7 @@ export default class HTMLOverlay extends BaseControl { return ( createElement('div', { - ref: this._onContainerLoad, + ref: this._containerRef, style }, this.props.redraw({ diff --git a/src/overlays/svg-overlay.js b/src/overlays/svg-overlay.js index 62ea741d..9f7a6f7f 100644 --- a/src/overlays/svg-overlay.js +++ b/src/overlays/svg-overlay.js @@ -47,7 +47,7 @@ export default class SVGOverlay extends BaseControl { createElement('svg', { width: viewport.width, height: viewport.height, - ref: this._onContainerLoad, + ref: this._containerRef, style }, this.props.redraw({