diff --git a/docs/components/marker.md b/docs/components/marker.md index d818ec3f..bebd830e 100644 --- a/docs/components/marker.md +++ b/docs/components/marker.md @@ -21,6 +21,41 @@ class Map extends Component { } ``` +Performance notes: if a large number of markers are needed, it's generally favorable to cache the `` nodes, so that we don't rerender them when the viewport changes. + +```js +import React, {Component} from 'react'; +import ReactMapGL, {Marker} from 'react-map-gl'; + +const CITIES = [...]; + +// React.memo ensures that the markers are only rerendered when data changes +const Markers = React.memo(({data}) => + data.map( + city => + ) +); + +class Map extends Component { + state = { + viewport: { + latitude: 37.78, + longitude: -122.41, + zoom: 8 + } + }; + + render() { + return ( + this.setState({viewport})}> + + + ); + } +} +``` + + ## Properties ##### `latitude` {Number} (required) diff --git a/examples/controls/src/app.js b/examples/controls/src/app.js index 11e3fdc0..8d6fd757 100644 --- a/examples/controls/src/app.js +++ b/examples/controls/src/app.js @@ -35,6 +35,8 @@ export default class App extends Component { bearing: 0, pitch: 0 }, + // Perf: the markers never change, avoid rerender when interacting with the map + markers: CITIES.map(this._renderCityMarker), popupInfo: null }; } @@ -71,7 +73,7 @@ export default class App extends Component { } render() { - const {viewport} = this.state; + const {viewport, markers} = this.state; return ( - {CITIES.map(this._renderCityMarker)} + {markers} {this._renderPopup()} diff --git a/src/components/marker.js b/src/components/marker.js index 44c44f41..bcac86cb 100644 --- a/src/components/marker.js +++ b/src/components/marker.js @@ -54,6 +54,8 @@ export default class Marker extends DraggableControl { static propTypes = propTypes; static defaultProps = defaultProps; + _control: any = null; + _getPosition(): [number, number] { const {longitude, latitude, offsetLeft, offsetTop} = this.props; const {dragPos, dragOffset} = this.state; @@ -71,26 +73,41 @@ export default class Marker extends DraggableControl { } _render() { - const {className, draggable} = this.props; - const {dragPos} = this.state; - const [x, y] = this._getPosition(); + const transform = `translate(${x}px, ${y}px)`; + const div = this._containerRef.current; - const containerStyle = { - position: 'absolute', - left: x, - top: y, - cursor: draggable ? (dragPos ? 'grabbing' : 'grab') : 'auto' - }; + if (this._control && div) { + // Perf: avoid rerendering if only the viewport changed + div.style.transform = transform; + } else { + const {className, draggable} = this.props; + const {dragPos} = this.state; - return ( -
- {this.props.children} -
- ); + const containerStyle = { + position: 'absolute', + left: 0, + top: 0, + transform, + cursor: draggable ? (dragPos ? 'grabbing' : 'grab') : 'auto' + }; + + this._control = ( +
+ {this.props.children} +
+ ); + } + return this._control; + } + + render() { + // invalidate cached element + this._control = null; + return super.render(); } }