'How to mutate props in Vue3 and have the parent computed setter called

I know this is an anti pattern, but it would simplify things a lot. And furthermore I'm just curious if it's possible. When I call test1() I get the following warning: Set operation on key "items" failed: target is readonly. and nothing else happens. When I call test2() items change within the child AND the parent, BUT the computed setter ist not called. Why?

Why is the computed setter not called? Is it possible to mutate the props within the child and still have the computed setter called?

I've got the following parent component:

<template>
  <div class="m-10 bg-white">
    parent: {{ items }}
    <br />
    <br />
    <TestComponent2 :items="items" />
  </div>
</template>

<script lang="ts" setup>
import { computed, ref } from "vue";
import TestComponent2 from "./TestComponent2.vue";

const container = ref({
  items: ["a", "b", "c"],
});

const items = computed({
  get: () => {
    return container.value.items;
  },
  set: (v) => {
    console.log("set", v);
    container.value.items = v;
  },
});
</script>

And this is the child component:

<template>
  <div>
    child: {{ items }}
    <br />
    <br />
    <button @click="test1()">test1</button>
    <br />
    <button @click="test2()">test2</button>
  </div>
</template>

<script lang="ts" setup>
import { defineProps, toRefs } from "vue";

const props = defineProps({
  items: {
    type: Array,
    required: true,
  },
});

const { items } = toRefs(props);

const test1 = () => {
  items.value = [];
};

const test2 = () => {
  items.value.pop();
};
</script>


Solution 1:[1]

You're likely looking for v-model:items="items".

The gist:

parent.vue:

<child v-model:items="items" />

child.vue:

emit('update:items', value);

Note: both items.pop() and items.push() will mutate the value in both parent and child, but they won't trigger an assignment.

For that, you have to:

const copy = [...items];
copy.push('new'); // or pop(), splice()...
// if inside child:
emit('update:items', copy);

Docs here

Working example here


Technically,

<child v-model:items="items" />

is shorthand for:

<child
  :items="items"
  @update:items="value => items = value"
/>

In you don't specify the child variable name (in our case items), it defaults to modelValue.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1