最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Vue, how to handle reactivity with deeply nested objects - Stack Overflow

programmeradmin0浏览0评论

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] or this.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
 |  Show 2 more ments

1 Answer 1

Reset to default 1

I 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

发布评论

评论列表(0)

  1. 暂无评论