Skip to content

Commit 7a2515e

Browse files
author
danhnguyen
committed
refactor: optimize reactivity with shallowRef and enhance map lifecycle management
1 parent 96c6bd1 commit 7a2515e

File tree

9 files changed

+50
-75
lines changed

9 files changed

+50
-75
lines changed

libs/components/FillLayer.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts" setup>
2-
import { ref, inject, watch, computed, onUnmounted } from 'vue';
2+
import { inject, watch, computed, onUnmounted, shallowRef } from 'vue';
33
import {
44
MapProvideKey,
55
SourceProvideKey,
@@ -93,8 +93,8 @@ const props = withDefaults(defineProps<Partial<LayerProps>>(), {
9393
const emits = defineEmits<Emits>();
9494
9595
// Injected dependencies
96-
const sourceData = inject(SourceProvideKey, ref(null));
97-
const mapInstance = inject(MapProvideKey, ref(null));
96+
const sourceData = inject(SourceProvideKey, shallowRef(null));
97+
const mapInstance = inject(MapProvideKey, shallowRef(null));
9898
9999
// Computed properties for better performance and reactivity
100100
const effectiveSource = computed(() => props.source || sourceData.value);

libs/components/GeoJsonSource.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts" setup>
2-
import { inject, ref, provide, computed, onUnmounted } from 'vue';
2+
import { inject, ref, provide, computed, onUnmounted, shallowRef } from 'vue';
33
import { MapProvideKey, SourceProvideKey } from '@libs/enums';
44
import {
55
useCreateGeoJsonSource,
@@ -62,9 +62,9 @@ const emits = defineEmits<Emits>();
6262
const { logError } = useLogger(props.debug);
6363
6464
// Reactive state management
65-
const mapInstance = inject(MapProvideKey, ref(null));
65+
const mapInstance = inject(MapProvideKey, shallowRef(null));
6666
const isSourceRegistered = ref(false);
67-
const lastDataUpdate = ref<GeoJSONSourceSpecification['data']>();
67+
const lastDataUpdate = shallowRef<GeoJSONSourceSpecification['data']>();
6868
6969
// Computed properties for better reactivity and performance
7070
const isDataValid = computed(() => {

libs/components/GeolocateControls.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
<script lang="ts" setup>
2-
import { inject, ref, watchEffect, computed, onUnmounted } from 'vue';
2+
import {
3+
inject,
4+
ref,
5+
watchEffect,
6+
computed,
7+
onUnmounted,
8+
shallowRef,
9+
} from 'vue';
310
import { MapProvideKey, GeolocateEvents } from '@libs/enums';
411
import {
512
useGeolocateControl,
@@ -66,7 +73,7 @@ const emits = defineEmits<Emits>();
6673
const { logError } = useLogger(props.debug);
6774
6875
// Reactive state management
69-
const mapInstance = inject(MapProvideKey, ref(null));
76+
const mapInstance = inject(MapProvideKey, shallowRef(null));
7077
const isControlRegistered = ref(false);
7178
const controlError = ref<any>(null);
7279

libs/components/Mapbox.vue

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
onBeforeMount,
1010
onUnmounted,
1111
watchEffect,
12+
shallowRef,
1213
} from 'vue';
1314
import { MapProvideKey, MapboxEvents, MapCreationStatus } from '@libs/enums';
1415
import {
@@ -146,7 +147,7 @@ const { logError } = useLogger(props.debug);
146147
147148
// Reactive state management
148149
const innerOptions = ref<Partial<MapOptions>>();
149-
const maplibreElRef = ref<HTMLElement>();
150+
const mapContainerRef = shallowRef<HTMLElement | null>(null);
150151
const styleRef = ref(props.options.style as string);
151152
152153
const isComponentMounted = ref(false);
@@ -159,12 +160,6 @@ const mapOptions = useOptimizedComputed(
159160
() => {
160161
const baseOptions = { ...props.options };
161162
const mergedOptions = { ...baseOptions, ...innerOptions.value };
162-
163-
// Ensure container is properly set
164-
if (!mergedOptions.container) {
165-
mergedOptions.container = props.containerId;
166-
}
167-
168163
return mergedOptions;
169164
},
170165
{
@@ -197,37 +192,6 @@ function setMapOptions(options: Partial<MapOptions>): void {
197192
}
198193
}
199194
200-
/**
201-
* Enhanced container creation with error handling and validation
202-
*/
203-
function createMapContainer(): HTMLElement | null {
204-
try {
205-
const wrapper = document.getElementById('maplibre_container');
206-
if (!wrapper) {
207-
logError('Map container wrapper not found');
208-
return null;
209-
}
210-
211-
// Remove existing container if present
212-
const existingContainer = document.getElementById(props.containerId);
213-
if (existingContainer) {
214-
existingContainer.remove();
215-
}
216-
217-
const container = document.createElement('div');
218-
container.id = props.containerId;
219-
container.className = props.containerClass;
220-
container.style.width = '100%';
221-
container.style.height = '100%';
222-
223-
wrapper.appendChild(container);
224-
return container;
225-
} catch (error) {
226-
logError('Error creating map container:', error);
227-
return null;
228-
}
229-
}
230-
231195
// Enhanced map creation with comprehensive error handling and performance monitoring
232196
const {
233197
mapInstance,
@@ -243,7 +207,7 @@ const {
243207
setMinPitch,
244208
setMinZoom,
245209
setRenderWorldCopies,
246-
} = useCreateMapbox(maplibreElRef, styleRef, {
210+
} = useCreateMapbox(mapContainerRef, styleRef, {
247211
...unref(mapOptions),
248212
register: (actions: CreateMaplibreActions) => {
249213
try {
@@ -367,14 +331,12 @@ watchEffect(async () => {
367331
try {
368332
await nextTick();
369333
370-
const container = createMapContainer();
371-
if (container) {
372-
maplibreElRef.value = container;
334+
if (mapContainerRef.value) {
373335
isComponentMounted.value = true;
374336
mapCreationStatus.value = MapCreationStatus.Initializing;
375337
} else {
376-
logError('Failed to create map container');
377-
mapCreationStatus.value = MapCreationStatus.Error;
338+
// Wait for next tick if container is not ready yet
339+
return;
378340
}
379341
} catch (error) {
380342
logError('Error in container creation watchEffect:', error);
@@ -389,9 +351,6 @@ function cleanup(): void {
389351
// Stop all watchers
390352
watchers.forEach((stopWatcher) => stopWatcher?.());
391353
392-
// Remove container
393-
maplibreElRef.value?.remove();
394-
395354
// Reset state
396355
isComponentMounted.value = false;
397356
mapCreationStatus.value = MapCreationStatus.Destroyed;
@@ -414,7 +373,11 @@ onUnmounted(() => {
414373
</script>
415374

416375
<template>
417-
<div id="maplibre_container">
376+
<div
377+
:id="containerId"
378+
ref="mapContainerRef"
379+
:class="['maplibre-container', containerClass]"
380+
>
418381
<!-- Loading state -->
419382
<div v-if="isMapLoading">
420383
<slot name="loading"> </slot>
@@ -437,9 +400,4 @@ onUnmounted(() => {
437400
height: 100%;
438401
overflow: hidden;
439402
}
440-
441-
// Legacy support
442-
#maplibre_container {
443-
@extend .maplibre-container;
444-
}
445403
</style>

libs/components/Marker.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts" setup>
2-
import { inject, ref, useSlots, watch, computed } from 'vue';
2+
import { inject, ref, useSlots, watch, computed, shallowRef } from 'vue';
33
import { MapProvideKey } from '@libs/enums';
44
import { useCreateMarker } from '@libs/composables';
55
import type { Anchor } from '@libs/types';
@@ -71,7 +71,7 @@ const emits = defineEmits<Emits>();
7171
const slots = useSlots();
7272
7373
// Injected dependencies
74-
const mapInstance = inject(MapProvideKey, ref(null));
74+
const mapInstance = inject(MapProvideKey, shallowRef(null));
7575
const markerElRef = ref<HTMLElement>();
7676
7777
// Computed properties for better performance

libs/components/Popup.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts" setup>
2-
import { inject, ref, watch, computed } from 'vue';
2+
import { inject, ref, watch, computed, shallowRef } from 'vue';
33
import { MapProvideKey } from '@libs/enums';
44
import { useCreatePopup } from '@libs/composables';
55
import type { LngLatLike, PopupOptions } from 'maplibre-gl';
@@ -57,7 +57,7 @@ const props = withDefaults(defineProps<Partial<PopupProps>>(), {
5757
const emits = defineEmits<Emits>();
5858
5959
// Injected dependencies
60-
const mapInstance = inject(MapProvideKey, ref(null));
60+
const mapInstance = inject(MapProvideKey, shallowRef(null));
6161
const popupElRef = ref<HTMLElement>();
6262
6363
// Computed properties for better performance

libs/composables/map/useCreateMapbox.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ interface SimplifiedCreateMaplibreActions extends CreateMaplibreActions {
5252
* @returns Enhanced actions and state for map management
5353
*/
5454
export function useCreateMapbox(
55-
elRef: MaybeRef<HTMLElement | undefined>,
55+
elRef: MaybeRef<HTMLElement | undefined | null>,
5656
styleRef: MaybeRef<StyleSpecification | string>,
5757
props: Omit<CreateMapboxProps, 'container' | 'style'> = {},
5858
) {
@@ -64,9 +64,10 @@ export function useCreateMapbox(
6464
const mapCreationStatus = ref<MapCreationStatus>(
6565
MapCreationStatus.NotInitialized,
6666
);
67-
const mapOptions = ref<Omit<MapOptions, 'container' | 'style'>>(options);
67+
const mapOptions =
68+
shallowRef<Omit<MapOptions, 'container' | 'style'>>(options);
6869
const retryCount = ref<number>(0);
69-
const currentStyle = ref<StyleSpecification | string | null>(null);
70+
const currentStyle = shallowRef<StyleSpecification | string | null>(null);
7071

7172
// Computed properties for better reactivity and performance
7273
const mapInstanceComputed = computed(() => mapInstance.value);
@@ -135,7 +136,7 @@ export function useCreateMapbox(
135136
const style = unref(styleRef);
136137

137138
if (!el) {
138-
mapCreationStatus.value = MapCreationStatus.Error;
139+
// Don't set error here, just wait for element to be available
139140
return;
140141
}
141142

@@ -144,6 +145,9 @@ export function useCreateMapbox(
144145
return;
145146
}
146147

148+
// Prevent double initialization
149+
if (mapInstance.value) return;
150+
147151
mapCreationStatus.value = MapCreationStatus.Initializing;
148152

149153
try {
@@ -236,7 +240,11 @@ export function useCreateMapbox(
236240

237241
try {
238242
mapInstance.value!.setCenter(centerVal);
239-
mapOptions.value.center = centerVal;
243+
// Update local state but avoid triggering watchers if possible
244+
const currentOpts = mapOptions.value;
245+
if (currentOpts.center !== centerVal) {
246+
mapOptions.value.center = centerVal;
247+
}
240248
} catch (error) {
241249
logError('Error setting map center:', error, { center: centerVal });
242250
}
@@ -458,15 +466,17 @@ export function useCreateMapbox(
458466

459467
// Watch effect for automatic map initialization
460468
const stopWatchEffect = watchEffect(() => {
461-
removeMap();
462-
if (!unref(mapInstance) && unref(elRef)) {
469+
const el = unref(elRef);
470+
if (el && !mapInstance.value) {
463471
initMap();
464-
stopWatchEffect();
472+
} else if (!el && mapInstance.value) {
473+
removeMap();
465474
}
466475
});
467476

468477
// Cleanup on unmount
469478
onUnmounted(() => {
479+
stopWatchEffect();
470480
destroyMap();
471481
});
472482

libs/composables/map/useGeoJsonSource.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function useGeoJsonSource(
5959
const { logError } = useLogger(debug);
6060

6161
// State management with better typing and performance
62-
const instanceRef = ref<CreateGeoJsonSourceActions>();
62+
const instanceRef = shallowRef<CreateGeoJsonSourceActions>();
6363
const sourceRef = shallowRef<Nullable<GeoJSONSource>>(null);
6464
const sourceIdRef = ref<string>();
6565
const sourceStatus = ref<GeoJsonSourceStatus>(

libs/composables/map/useLayer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export function useLayer<T extends LayerSpecification>(
7777
const { debug = false, autoCleanup = true } = props;
7878
const { logError } = useLogger(debug);
7979

80-
const instanceRef = ref<CreateLayerActions<T>>();
80+
const instanceRef = shallowRef<CreateLayerActions<T>>();
8181
const layerStatus = ref<LayerManagementStatus>(
8282
LayerManagementStatus.NotRegistered,
8383
);

0 commit comments

Comments
 (0)