I have an Object that I'm manipulating as shown below:
const posts = [
{ key1: "abc", key2: [{ innerKey1: "def", innerKey2: 123 }, { innerKey1: "def", innerKey2: 123 }] },
{ key1: "abc", key2: [{ innerKey1: "def", innerKey2: 123 }, { innerKey1: "def", innerKey2: 123 }] },
{ key1: "abc", key2: [{ innerKey1: "def", innerKey2: 123 }, { innerKey1: "def", innerKey2: 123 }] },
];
posts[0].key2[1].innerKey1 = "newVal";
The Array in key2
is a prop where the inner keys are expected to mutate. I want to maintain reactivity when innerKey1
or innerKey2
change.
Because Vue has difficulty detecting changes in Arrays and Objects as discussed here, we cannot modify Arrays by their indexes or Objects by their keys directly.
In order to retain reactivity, it seems we need some plex logic using Vue.set()
to set newVal
:
I'm thinking along the lines of:
Vue.set(posts, 1, Vue.set(key2, 0, Vue.set(innerKey1, "newVal")))
Is this the best way to maintain reactivity here, or is there something I'm missing that can make this easier?
Note:
I've also tried using a deep watcher in my child ponent to observe changes that occur in the key2
array. But it doesn't seem capable of observing those changes.
//ChildComponent.vue
<html>
<div>
<RandomComponent v-for="thing in key2" :key... :innerKey2="thing.innerKey2">
</div>
</html>
<script>
props: {
key2: {
type: Array,
require: true,
},
},
watch: {
key2: {
immediate: true,
deep: true,
handler () {
console.log("Watcher working"); //Does not fire when parent mutates innerKeys
},
},
},
</script>
I have an Object that I'm manipulating as shown below:
const posts = [
{ key1: "abc", key2: [{ innerKey1: "def", innerKey2: 123 }, { innerKey1: "def", innerKey2: 123 }] },
{ key1: "abc", key2: [{ innerKey1: "def", innerKey2: 123 }, { innerKey1: "def", innerKey2: 123 }] },
{ key1: "abc", key2: [{ innerKey1: "def", innerKey2: 123 }, { innerKey1: "def", innerKey2: 123 }] },
];
posts[0].key2[1].innerKey1 = "newVal";
The Array in key2
is a prop where the inner keys are expected to mutate. I want to maintain reactivity when innerKey1
or innerKey2
change.
Because Vue has difficulty detecting changes in Arrays and Objects as discussed here, we cannot modify Arrays by their indexes or Objects by their keys directly.
In order to retain reactivity, it seems we need some plex logic using Vue.set()
to set newVal
:
I'm thinking along the lines of:
Vue.set(posts, 1, Vue.set(key2, 0, Vue.set(innerKey1, "newVal")))
Is this the best way to maintain reactivity here, or is there something I'm missing that can make this easier?
Note:
I've also tried using a deep watcher in my child ponent to observe changes that occur in the key2
array. But it doesn't seem capable of observing those changes.
//ChildComponent.vue
<html>
<div>
<RandomComponent v-for="thing in key2" :key... :innerKey2="thing.innerKey2">
</div>
</html>
<script>
props: {
key2: {
type: Array,
require: true,
},
},
watch: {
key2: {
immediate: true,
deep: true,
handler () {
console.log("Watcher working"); //Does not fire when parent mutates innerKeys
},
},
},
</script>
Share
Improve this question
edited Jul 14, 2022 at 5:51
tony19
139k23 gold badges278 silver badges348 bronze badges
asked Jun 18, 2021 at 19:17
MattMatt
1,7255 gold badges31 silver badges64 bronze badges
7
- Modifying arrays by indexes is a mistake here, this doesn't trigger reactivity. See vuejs/v2/guide/reactivity.html#For-Arrays – Estus Flask Commented Jun 18, 2021 at 19:37
-
1
Yeah, that's what I said in my post. That's why I suggested replacing it with
Vue.set(posts, 1, Vue.set(key2, 0, Vue.set(innerKey1, "newVal")))
and linked that same page. I'll clarify that some more, it seems I wasn't clear – Matt Commented Jun 18, 2021 at 19:38 -
It's that, or using array methods to replace an element, i.e. with
splice
. – Estus Flask Commented Jun 18, 2021 at 19:39 -
sometimes we also see things like this:
this.array = [...this.array, newItem]
orthis.array = this.array.filter(etc)
– The Fool Commented Jun 18, 2021 at 19:44 -
1
do the same technique but start higher up.
this.outerData = plexFuncToGetNewOuterData(this.outerData)
– The Fool Commented Jun 18, 2021 at 19:47
1 Answer
Reset to default 1I sense a lack of clarity in the question.
- If you want to update just a property of an existing array element mutating by index should work fine without any workaround.
i.e This should be reactive
posts[0].key2[1].innerKey1 = "newVal";
- However, If you are trying to replace the array element you need a workaround for the Vue reactivity issue explained here
I think the cleanest workaround for your case is using Array.splice
to replace the element. (for example after network response)
const postId = 0 // the root element
const updatePost = { ...this.posts[postId] }
updatePost.key2[1].innerKey1 = 'newVal'
this.posts.splice(postId, 1, updatedPost);
Here is an illustration I made on codesanbox