tcpiper
tcpiper

Reputation: 2544

Why vuex actions in updated hooks trigger infinite updated events?

I just don't understand, in list.vue I only trigger an list action that asynchronously alters state.list in updated hook. items is a computed property that only relies on state.list. Why would this lead to infinite updated events?

I've found that if I move the code in updated hook to watch option like this:

watch: {
  '$route': function () {
    this.$store.dispatch('list') 
  },
},

infinite problem would disappear. But this would trigger updated hook twice every time $route change is watched, which I also don't know Why.

Simple demo is here. Related code

// main.js
var Vue = require('vue')
var app = require('./App.vue')

new Vue(app).$mount('#app')

// App.vue
<template>
<div id="app">
  <h1>list bug test</h1>
  <router-view></router-view> 
</div>
</template>

<script>
import store from './store.js'
import router from './router.js'

export default {
  store,
  router,
}
</script>

// list.vue
<template>
<table>
  <tbody>
  <tr>
    <th>title</th>
    <th>actions</th>
  </tr>
  <tr v-for="(item, index) in items">
    <td> {{item.title}} </td>
    <td> submit </td>
  </tr>
  </tbody>
</table>
</template>

<script>
export default {
  updated: function () {
    console.log('items.vue updated')
    this.$store.dispatch('list') 
  },
  mounted: function () {
    console.log('items.vue mounted')
    this.$store.dispatch('list')
  },
  computed: {
    items: function () {
    return this.$store.state.list.map(e => ( {title: e.ksmc } )) 
    },
  },
}
</script>

// router.js
var router = new VueRouter({
  routes:[
    {
      path:'/', 
      name: 'list',
      component: listView,
    },
  ],
})

// store.js
var store = new Vuex.Store({
  state: {
    error: undefined,
    list: JSON.parse(localStorage.getItem('list')) || [],
  },
  mutations: {
    list: function(state, list) {
      state.list = list
    },
    error: function(state, error) {
      state.error = error
    },
  },
  actions: {
    list (ctx, kwargs) {
      setTimeout(() => {
        ctx.commit('list', [{ksmc:'this is a title'}])
      }, 1000)
    },
  },
})

Upvotes: 0

Views: 1137

Answers (1)

Decade Moon
Decade Moon

Reputation: 34306

The updated hook is called after the component's DOM has been updated due to a change in the component's data model (to cause the component to be re-rendered). Therefore you shouldn't change the component's state (async or not) inside this hook otherwise the state change will cause the component to re-render which will fire the updated hook which will change the state... and so on.

The docs explain it well:

The component’s DOM will have been updated when this hook is called, so you can perform DOM-dependent operations here. However, in most cases you should avoid changing state inside the hook. To react to state changes, it’s usually better to use a computed property or watcher instead.

Upvotes: 2

Related Questions