From 4238ce093834e68c98ec64552c52c010f2c170c9 Mon Sep 17 00:00:00 2001 From: evlist Date: Wed, 19 Oct 2022 14:53:22 +0200 Subject: [PATCH] Using maps instead of objects to store tiles and removing the reinititialization when TiledLayer properties are updated. --- src/components/map/Map.test.tsx | 138 +++++++++++++++++++++++++ src/components/map/TiledLayer.test.tsx | 23 ++++- src/components/map/TiledLayer.tsx | 14 +-- 3 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 src/components/map/Map.test.tsx diff --git a/src/components/map/Map.test.tsx b/src/components/map/Map.test.tsx new file mode 100644 index 0000000..e126c0f --- /dev/null +++ b/src/components/map/Map.test.tsx @@ -0,0 +1,138 @@ +import { renderHook, act } from '@testing-library/react'; +import { useAtom } from 'jotai'; +import { coordinateSystemAtom, relativeCoordinateSystemAtom } from './Map'; + +describe('The Map component coordinate system', () => { + test('is initialized', () => { + const { result } = renderHook(() => useAtom(coordinateSystemAtom)); + expect(result.current[0]).toMatchInlineSnapshot(` +Object { + "shift": Object { + "x": 0, + "y": 0, + }, + "zoom": 1, +} +`); + }); + + test('reacts to a translation', () => { + const { result } = renderHook(() => [ + useAtom(coordinateSystemAtom), + useAtom(relativeCoordinateSystemAtom), + ]); + act(() => { + result.current[0][1]({ + shift: { + x: 0, + y: 0, + }, + zoom: 1, + } as any); + result.current[1][1]({ + deltaShift: { x: 5, y: 1 }, + zoomCenter: null, + deltaZoom: null, + } as any); + }); + expect(result.current[0][0]).toMatchInlineSnapshot(` +Object { + "shift": Object { + "x": 5, + "y": 1, + }, + "zoom": 1, +} +`); + }); + + test('reacts to a zoom on its origin', () => { + const { result } = renderHook(() => [ + useAtom(coordinateSystemAtom), + useAtom(relativeCoordinateSystemAtom), + ]); + act(() => { + result.current[0][1]({ + shift: { + x: 0, + y: 0, + }, + zoom: 1, + } as any); + result.current[1][1]({ + deltaShift: null, + zoomCenter: null, + deltaZoom: 5, + } as any); + }); + expect(result.current[0][0]).toMatchInlineSnapshot(` +Object { + "shift": Object { + "x": 0, + "y": 0, + }, + "zoom": 5, +} +`); + }); + + test('reacts to a zoom around another point', () => { + const { result } = renderHook(() => [ + useAtom(coordinateSystemAtom), + useAtom(relativeCoordinateSystemAtom), + ]); + act(() => { + result.current[0][1]({ + shift: { + x: 0, + y: 0, + }, + zoom: 1, + } as any); + result.current[1][1]({ + deltaShift: null, + zoomCenter: { x: 2, y: 2 }, + deltaZoom: 5, + } as any); + }); + expect(result.current[0][0]).toMatchInlineSnapshot(` +Object { + "shift": Object { + "x": -8, + "y": -8, + }, + "zoom": 5, +} +`); + }); + + test('reacts to a zoom (around another point) and a translation', () => { + const { result } = renderHook(() => [ + useAtom(coordinateSystemAtom), + useAtom(relativeCoordinateSystemAtom), + ]); + act(() => { + result.current[0][1]({ + shift: { + x: 0, + y: 0, + }, + zoom: 1, + } as any); + result.current[1][1]({ + deltaShift: { x: 1, y: 3 }, + zoomCenter: { x: 2, y: 2 }, + deltaZoom: 5, + } as any); + }); + expect(result.current[0][0]).toMatchInlineSnapshot(` +Object { + "shift": Object { + "x": -7, + "y": -5, + }, + "zoom": 5, +} +`); + }); +}); diff --git a/src/components/map/TiledLayer.test.tsx b/src/components/map/TiledLayer.test.tsx index e6f7227..b6fe5d0 100644 --- a/src/components/map/TiledLayer.test.tsx +++ b/src/components/map/TiledLayer.test.tsx @@ -120,7 +120,7 @@ describe('The TiledLayer component ', () => { `); }); - test('is reinitialized if its key isObject updated', () => { + test('is not reinitialized if its key isObject updated', () => { const { baseElement, rerender } = render( {
+ + + {"provider":"osm","zoomLevel":10,"x":6,"y":10} + + + + + {"provider":"osm","zoomLevel":10,"x":7,"y":10} + + + + + {"provider":"osm","zoomLevel":10,"x":8,"y":10} + + diff --git a/src/components/map/TiledLayer.tsx b/src/components/map/TiledLayer.tsx index f7d5ad1..57f4021 100644 --- a/src/components/map/TiledLayer.tsx +++ b/src/components/map/TiledLayer.tsx @@ -30,12 +30,7 @@ export interface TiledLayerProperties { export const TiledLayer: react.FC = memo( (props: TiledLayerProperties) => { console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`); - const tiles = useRef({}); - const previousKeyObject = useRef(props.keyObject); - if (!isEqual(props.keyObject, previousKeyObject.current)) { - previousKeyObject.current = props.keyObject; - tiles.current = {}; - } + const tiles = useRef(new Map()); range(props.viewPort.topLeft.y, props.viewPort.bottomRight.y + 1).map( (row) => { @@ -48,8 +43,9 @@ export const TiledLayer: react.FC = memo( y: props.keyObject.y + row, }; const key = tileUri(keyObject); - if (!Object.hasOwn(tiles.current, key)) { - tiles.current[key] = ( + if (!tiles.current.has(key)) { + tiles.current.set( + key, {props.tileFactory(keyObject)} @@ -60,7 +56,7 @@ export const TiledLayer: react.FC = memo( } ); - return <>{Object.values(tiles.current)}; + return <>{Array.from(tiles.current.values())}; }, isEqual );