search

Home  >  Q&A  >  body text

Vue Compute() not firing on reactive map

I have a reactivity around the initially empty map: const map =reactive({});, and a calculation which tells If the map has a key "key": const mapContainsKeyCompulated = Computed(() => map.hasOwnProperty("key")). When I change the map, the calculations don't update.

I was stuck on this problem for a day and managed to come up with a minimal example that demonstrates the problem:

<script setup>
import {computed, reactive, ref, watch} from "vue";

const map = reactive({});
const key = "key";

const mapContainsKeyComputed = computed(() => map.hasOwnProperty(key))

const mapContainsKeyWatched = ref(map.hasOwnProperty(key));
watch(map, () => mapContainsKeyWatched.value = map.hasOwnProperty(key))
</script>

<template>
  Map: {{map}}
  <br/>
  Computed: does map contain "key"? {{mapContainsKeyComputed}}
  <br/>
  Watch: does map contain key? {{mapContainsKeyWatched}}
  <br/>
  <button @click="map[key] = 'value'">add key-value</button>
</template>

I've read a bunch of stackoverflow answers and the Vue documentation but I still can't figure it out.

Edit: As @estus-flask mentioned, this is a VueJS bug fixed in 3.2.46.

P粉127901279P粉127901279244 days ago379

reply all(1)I'll reply

  • P粉668146636

    P粉6681466362024-03-28 00:34:51

    Vue reactivity requires explicit support for reactive object methods. hasOwnProperty is quite low-level, so it has been unsupported for some time. Without support, map.hasOwnProperty(key) will attempt to access the key on the non-reactive original object, and reactivity will not be triggered, so the first computation< /code> The call does not set a listener that can be fired the next time map changes.

    One way to solve this problem is to first define the key (as suggested in another answer), which is the traditional way to make reactivity work in Vue 2 and 3:

    const map = reactive({ key: undefined })

    An alternative is to access the missing key property on the reactive object:

    const mapContainsKeyComputed = computed(() => map[key] !== undefined)

    Another way is to use the in operator. Since Vue 3 responds using Proxy, it can detect that the property is accessed via the has trap:

    const mapContainsKeyComputed = computed(() => key in map)

    Support for hasOwnProperty has been recently added in 3.2.46, so the code in the question should work in the latest Vue version.

    map is not a real map. This will be different in any Vue 3 version if using Map, Vue supports it and it is expected that map.has(key) will trigger reactivity.

    reply
    0
  • Cancelreply