diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2020-09-04 10:10:18 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2020-09-10 10:03:47 +0200 |
| commit | 080ddf5c161c86a2374d0e38e7a9072b0401a607 (patch) | |
| tree | 0910bf71dc4793ad9a91b16f98ca5c6b8cfd8a0a | |
| parent | 0c1c4f978e1cd0e73ea8e61eca86f304ad141733 (diff) | |
| download | mullvadvpn-080ddf5c161c86a2374d0e38e7a9072b0401a607.tar.xz mullvadvpn-080ddf5c161c86a2374d0e38e7a9072b0401a607.zip | |
Optimize SvgMap rendering cycles
| -rw-r--r-- | gui/src/renderer/components/SvgMap.tsx | 53 |
1 files changed, 38 insertions, 15 deletions
diff --git a/gui/src/renderer/components/SvgMap.tsx b/gui/src/renderer/components/SvgMap.tsx index aa2a8d430b..27e9c0c6b4 100644 --- a/gui/src/renderer/components/SvgMap.tsx +++ b/gui/src/renderer/components/SvgMap.tsx @@ -1,6 +1,6 @@ import { geoMercator, GeoProjection } from 'd3-geo'; import rbush from 'rbush'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { ComposableMap, Geographies, Geography, Marker, ZoomableGroup } from 'react-simple-maps'; import geographyData from '../../../assets/geo/geometry.json'; @@ -124,6 +124,35 @@ function getCombindedViewportBboxMatch(viewportBboxes: BBox[]) { }; } +function sameProps(prevProps: IProps, nextProps: IProps) { + return ( + prevProps.width === nextProps.width && + prevProps.height === nextProps.height && + prevProps.center[0] === nextProps.center[0] && + prevProps.center[1] === nextProps.center[1] && + prevProps.offset[0] === nextProps.offset[0] && + prevProps.offset[1] === nextProps.offset[1] && + prevProps.zoomLevel === nextProps.zoomLevel && + prevProps.showMarker === nextProps.showMarker && + prevProps.markerImagePath === nextProps.markerImagePath + ); +} + +function useBboxes(bbox: BBox): [BBox[], () => void] { + const prev = useRef<BBox[]>([]); + const bboxes = useMemo(() => [...prev.current, bbox], [bbox]); + + const keepLast = useCallback(() => { + prev.current = prev.current.slice(-1); + }, []); + + useEffect(() => { + prev.current = [...bboxes]; + }, [bboxes]); + + return [bboxes, keepLast]; +} + export interface IProps { width: number; height: number; @@ -135,29 +164,29 @@ export interface IProps { } // @TODO: Calculate zoom level based on (center + span) (aka MKCoordinateSpan) -export default function SvgMap(props: IProps) { +function SvgMap(props: IProps) { const { width, height, zoomLevel } = props; const center = useMemo(() => props.center, [...props.center]); const offset = useMemo(() => props.offset, [...props.offset]); - const [viewportBboxes, setViewportBboxes] = useState<BBox[]>([]); const projection = useMemo(() => getProjection(width, height, offset, projectionConfig.scale), [ width, height, - ...offset, + offset, projectionConfig.scale, ]); const zoomCenter = useMemo(() => getZoomCenter(center, offset, projection, zoomLevel), [ - ...center, - ...offset, + center, + offset, projection, zoomLevel, ]); const viewportBbox = useMemo( () => getViewportGeoBoundingBox(zoomCenter, width, height, projection, zoomLevel), - [...zoomCenter, width, height, projection, zoomLevel], + [zoomCenter, width, height, projection, zoomLevel], ); + const [viewportBboxes, removeOldViewportBboxes] = useBboxes(viewportBbox); const combinedViewportBboxMatch = useMemo(() => getCombindedViewportBboxMatch(viewportBboxes), [ viewportBboxes, @@ -170,14 +199,6 @@ export default function SvgMap(props: IProps) { [combinedViewportBboxMatch], ); - const removeOldViewportBboxes = useCallback(() => { - setViewportBboxes((viewportBboxes) => viewportBboxes.slice(-1)); - }, []); - - useEffect(() => { - setViewportBboxes((viewportBboxes) => [...viewportBboxes, viewportBbox]); - }, [viewportBbox]); - return ( <ComposableMap width={width} @@ -232,3 +253,5 @@ export default function SvgMap(props: IProps) { </ComposableMap> ); } + +export default React.memo(SvgMap, sameProps); |
