6

I’m passing a map to a component and then emit back new values and update the map. This triggers the watch on the map, but the old and new values passed are the same.

Is it possible in this case to have a watch that receives old values?

(function() {

const ui = {
setup() {
  const map = reactive(new Map([
    ['width', 800],
    ['height', 400]
  ]));

  function onCompUpdate(evt) {
    console.log(map.get('width'), map.get('height'), evt[0], evt[1]);
map.set('width', Number(evt[0]));
    map.set('height', Number(evt[1]));
  }
  
  watch(map, (n,o)=>{
  console.log(n.get('width'), n.get('height'), 
  o.get('width'), o.get('height'));
  });

  return { map, onCompUpdate };
  },
};

const vueApp = createApp(ui);

vueApp.component('base-input', {
props: ['some' ], emits: ['comp-update'],
setup(props, { emit }) {

  let chars = ref(Array.from(props.some.values()).join(','));

  function checkInput(val) {
    emit('comp-update', val.split(','));
    chars.value = val;
  }

  return { chars, checkInput };
},
template: `<input type="text" spellcheck="false" :value="chars"
input="checkInput($event.target.value)"/>`
});

vueApp.mount('#vue');

})();

HTML:

<div id="vue">
  <base-input :some="map" @comp-update="onCompUpdate($event)"></base-input>
</div>

Fiddle: https://jsfiddle.net/tfoller/sm28u7r0/35/

Dan
  • 59,490
  • 13
  • 101
  • 110

2 Answers2

3

It only triggers the watch if the whole Map is replaced. If you want to watch the mutation (adding/removing items), you need to set deep: true like so:

watch(myMap, () => {
  // myMap was mutated
}, { deep: true })

Working SFC Playground example

Marvin
  • 41
  • 4
1

This happens because the references to old and new are the same. Here's one workaround. Instead of watching the reference directly, watch the spreading of the reference, which does change. It has to be done inside a function expression since the spread is not reactive:

watch(() => [...map], (nArray, oArray)=>{
    const n = new Map(nArray);
    const o = new Map(oArray);
    console.log(n.get('width'), n.get('height'), o.get('width'), o.get('height'));
});

Since the spread converts the map to an array, inside the watch you can convert the values back to maps.

Here is your updated fiddle

Dan
  • 59,490
  • 13
  • 101
  • 110