简体   繁体   中英

Changed state through Vuex mutation using Lodash.remove does not trigger a reactive re-render in my Vue component

I have a component that receives an array of Villager into it's prop from a parent component. The parent component pulls that array from this.$store.state .

return this.$store.state.villages.find(value => value.id === 0).villagers

I execute a this.$store.mutation commit to alter the given array afterwards. The mutation does work as you can see in the screenshot further down. However the component I am in is not re-rendering and is still showing 3 instead of 2 items.

The reactive chain seems to be broken somewhere but I can not pinpoint the fiend. I thought the props value is a reactive mechanism. As you can see in the screenshot it's value has changed but the DOM was not updated accordingly.

在此处输入图像描述

Code Excerpts

I tried to extract the relevant parts for the questions

store/index.ts

[...]
export default new Vuex.Store({
  state: {
    villages: [
      {
        id: 0,
        name: 'Peniuli',
        foundingDate: 20,
        villagers: [
          {
            id: '33b07765-0ec6-4600-aeb1-43187c362c5a1',
            name: 'Baltasar',
            bloodline: 'Gent',
            occupation: 'Farmer'
          },
[...]
   mutations: {
       disbandVillager (state, payload) {
         const villagerId = payload.id
         const village = state.villages.find(value => value.id === 0)
         console.debug('disbanding villager:', villagerId)
         if (!_.isNil(village)) {
           console.debug('bfore:', village.villagers)
           _.remove(village.villagers, function (n) {
             return n.id === villagerId
           })
           console.debug('after:', village.villagers)
         }
       }
     },
[...]

Village.vue

<template>
    <Villagers :villagers="playerVillage.villagers"></Villagers>
</template>
[...]
  computed: {
    playerVillage: function () {
      return this.$store.state.villages.find(value => value.id === 0)
    }
  }
[...]

Villagers.vue

<template>
  <v-container>
    <v-card v-for="villager in villagers" :key="villager.id">
      <v-row>
        <v-col>
          <v-card-title>Name: {{villager.name}}</v-card-title>
        </v-col>
        <v-col>
          <v-card-title>Bloodline: {{villager.bloodline}}</v-card-title>
        </v-col>
        <v-col>
          <v-card-title>Occupation: {{villager.occupation}}</v-card-title>
        </v-col>
        <v-col v-if="managable">
          <DisbandButton :villager="villager"></DisbandButton>
        </v-col>
      </v-row>
    </v-card>
  </v-container>
</template>

<script>
import DisbandButton from '@/components/village/DisbandButton'

export default {
  name: 'Villagers',
  components: { DisbandButton },
  props: [
    'villagers',
    'managable'
  ]
}
</script>

DisbandButton.vue

<template>
  <v-btn @click="disbandVillager" color="red">Disband</v-btn>
</template>

<script>
export default {
  name: 'DisbandButton',
  props: ['villager'],
  methods: {
    disbandVillager: function () {
      this.$store.commit('disbandVillager', { id: this.villager.id })
    }
  }
}
</script>

Assuming that's lodash then I believe the problem is that you're using _.remove .

Internally _.remove is implemented in such a way that it doesn't trigger Vue's reactivity system. Specifically here:

https://github.com/lodash/lodash/blob/ded9bc66583ed0b4e3b7dc906206d40757b4a90a/lodash.js#L3859

That's using the default Array splice method, not the custom override provided by Vue.

Something like this should work:

village.villagers = village.villagers.filter(function (n) {
  return n.id !== villagerId
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM