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.
Map:{{map}}
(line 14) update fine? Edit: As @estus-flask mentioned, this is a VueJS bug fixed in 3.2.46.
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.