marker component perf (#949)

This commit is contained in:
Xiaoji Chen 2019-12-17 17:42:36 -08:00 committed by GitHub
parent ec129a09b2
commit 4af4183ee6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 74 additions and 20 deletions

View File

@ -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 `<Marker>` 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 => <Marker key={city.name} longitude={city.longitude} latitude={city.latitude} ><img src="pin.png" /></Marker>
)
);
class Map extends Component {
state = {
viewport: {
latitude: 37.78,
longitude: -122.41,
zoom: 8
}
};
render() {
return (
<ReactMapGL {...this.state.viewport} onViewportChange={viewport => this.setState({viewport})}>
<Markers data={CITIES} />
</ReactMapGL>
);
}
}
```
## Properties
##### `latitude` {Number} (required)

View File

@ -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 (
<MapGL
@ -82,7 +84,7 @@ export default class App extends Component {
onViewportChange={this._updateViewport}
mapboxApiAccessToken={TOKEN}
>
{CITIES.map(this._renderCityMarker)}
{markers}
{this._renderPopup()}

View File

@ -54,6 +54,8 @@ export default class Marker extends DraggableControl<MarkerProps> {
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<MarkerProps> {
}
_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 (
<div
className={`mapboxgl-marker ${className}`}
ref={this._containerRef}
style={containerStyle}
>
{this.props.children}
</div>
);
const containerStyle = {
position: 'absolute',
left: 0,
top: 0,
transform,
cursor: draggable ? (dragPos ? 'grabbing' : 'grab') : 'auto'
};
this._control = (
<div
className={`mapboxgl-marker ${className}`}
ref={this._containerRef}
style={containerStyle}
>
{this.props.children}
</div>
);
}
return this._control;
}
render() {
// invalidate cached element
this._control = null;
return super.render();
}
}