Does Vue support reactivity on Map and Set data types?

Vue.js does not support reactivity on Map and Set data types (yet?).

The feature ticket has some discussion and this work around (by user "inca"):

Sets and Maps are not observable by Vue. In order to use those — either in v-for, or in computed properties, methods, watchers, template expressions, etc. — you need to create a serializable replica of this structure and expose it to Vue. Here's a naive example which uses a simple counter for providing Vue with information that Set is updated:

data() {
  mySetChangeTracker: 1,
  mySet: new Set(),
},

computed: {
  mySetAsList() { 
    // By using `mySetChangeTracker` we tell Vue that this property depends on it,
    // so it gets re-evaluated whenever `mySetChangeTracker` changes
    return this.mySetChangeTracker && Array.from(this.mySet);
  },
},

methods: {
  add(item) {
    this.mySet.add(item);
    // Trigger Vue updates
    this.mySetChangeTracker += 1;
  }
}

This illustrates a kinda hacky but 100% working method for making non-observable data reactive. Still, in real world cases I ended up with serialized versions of Sets/Maps (e.g. you'd probably want to store the modified versions of sets/maps in localstorage and thus serialize them anyway), so no artificial counters/hacks were involved.


As far as I know, Vue's reactivity tracks assignments. If you perform an assignment on your set, it should track reactivity, for example:

methods: {
  add(item) {
    this.mySet = new Set(this.mySet.add(item));
  }
}

This gives you a cleaner code, but with an obvious problem: performance.

Just pick your solution according to your needs :)


Vue 3

Yes, it does.

Vue 3 docs cover these at Reactivity in depth and Basic Reactivity APIs.

There are four kinds of "reactive" and the "reference" in addition. I'm still trying to find the usage patterns that suit me, after 6+ months with Vue 3 and ES6 coding. The main question is, whether to use Reactive or Reference.

Reference

Reference is the easy way to go. This is what gets returned by computed, for example. It makes a reactive version of one's JavaScript value, e.g. a Map or a Set.

There's a gotcha. If one uses Reference in one's JavaScript, a .value needs to be added to dereference the value. Using it in template HTML does not need that addition. This makes Reference perfect for UI facing things, but less so for internal programming.

I currently add Ref postfix to the name of any value or function that provides a Reference. That's just me. It's easy to get confused if one uses both Reference and Reactive (TypeScript would help, here).

Reactive

Reactive is made for Map-like use. One can initialise it as:

const rve = reactive( new Map() )

Accessing such does not need the .value. However, it seems Reactive does not have the enumeration methods (e.g. .entries()) that would allow it to be used Map-like. Therefore, it seems aimed at a use case where you know the keys of an object. But this may change.

I wish Reactive was brought in the direction that it can be used as a 1:1 replacement for ES Map. This would make it easy for me: Reactive for Maps and Reference for the rest.

I would also wish the name would change, to bring them closer. RMap would be fine - maybe I'll make one (derive from Reactive and add the enumeration methods).

Summary

The strong answer, with Vue 3, is "YES".

However, the developer guidance can be made more straightforward, clearly stating which would be the logic for picking Reference or Reactive, and what eg. their runtime pros and cons are, without needing to read various blog posts.


Edit: My current leaning is towards Ref, but I try to unwrap the reactivity quite early within the code, leading to just one .value within a computed.

Tags:

Vue.Js