Vue Compute() not firing on reactive map
P粉127901279
P粉127901279 2024-03-27 10:36:32
0
1
378

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.

  • Why is mapContainsKeyCompulated not updated?
  • If reactive doesn't "track" the addition or removal of keys to the map, why does Map:{{map}} (line 14) update fine?
  • When I replace map{} with array[] and replace "hasOwnProperty" with "includes()", it works fine. what is the difference?
  • How to solve this problem without using the ugly "watch" solution (where "map.hasOwnProperty(key)" must be repeated)?

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

P粉127901279
P粉127901279

reply all(1)
P粉668146636

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 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.

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template