2

I am loading markers depending on the bounding box. they come from an api via a pinia store. I am putting them in props field of my map wrapper component map-view. the markers are circlemarkers renderd via a vue-for loop.

when I move around on the map, the props field changes according to the state (itemsList) and so the vue-loop for the circlemarkers reloads the markers and new markers on the map. this works really nicely.

but after some time I found out, that when I click on a marker, a different marker-popup - than that of the clicked opens on that other mark. sometimes the whole map jumps to a completely different location because of this.

Is there any known problems with the order of markers and popups in leaflet, which leads to such problems? Can anybody imagine what makes that behavior? did I miss something?

here is my reduced vue map wrapper component:

<template>
 <div id="main-map-wrapper">
  <l-map ref="map" @update:bounds="updateBounds">
    <l-circle-marker v-for="(item, index) in itemsList" :key="index">
      <l-popup>{{item.name}}</l-popup>
    <l-circle-marker>
  </l-map> 
 </div>
</template>  

<script>
import { LCircleMarker, LMap, LPopup } from "@vue-leaflet/vue-leaflet"
import {useItemsStore} from "@/store/itemsStore"; //pinia store

export default defineComponent({
    name: "mapView",
    components: {LCircleMarker, LMap, LPopup},
    props: {
      itemsList: Object
    },
    methods: {
       updateBounds(bounds) {
            useItemsStore.resetToBounds(bounds) //updates the itemsList, which comes from parent
       }
    }
}
</script>

So itemsList is reactive and comes from the parent component.

I am using vue3.

0

1 Answer 1

3

The problem is how you are instructing Vue to track the list items.

In more detail, it has to do with :key="index", which tells Vue: "the unique identifier for each marker is its position in the markers array".
Assuming Vue initially renders a list of markers of n elements, when you update the list with different elements, the first n elements from the updated list are not updated/replaced, because Vue looks at their position in the array and, since it didn't change, the DOM elements do not need updating/re-rendering.

Relevant documentation.

To fix this, you have to use a unique identifier for each marker (the location's id, a generated uuid, or similar). Once you key by an actual unique identifier, Vue will be able to track the list items correctly (figure out which DOM elements need to be replaced and which ones have changed the position in the array).


Why?

By design, Vue requires a key for list rendering, which allows it to optimise the use and re-use of DOM elements when the list is updated. Vue documentation recommends using a primitive value as key (String, Number or Symbol).
When a :key is not specified, Vue uses the index as key, and issues a warning in development mode about the exact problem you're experiencing (potential problems tracking list updates).

When specifying :key="index" on the element you are suppressing that warning because you're explicitly declaring to Vue: "I'm OK with using the default behavior (index as unique identifier), despite its shortcomings (because I'm not going to update this list over the lifecycle of the parent component)".

Sign up to request clarification or add additional context in comments.

1 Comment

This is a great and complete answer. Thanks @tao for your generosity!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.