lamtacvu
lamtacvu

Reputation: 685

Vuex global state change does not trigger re-render component in v-for loop in Nuxt

I have difficult to use vuex global state combine with re-render child-component in Vue.js. The global state is mutated but does not re-render its data in v-for loop.

All list of data is rendered, but when the new data changes, component in /blog does not change data in it.

Here is some code:

/store/index.js

export const state = () => ({
  allData: [],
})
export const getters = {
  getAllData: (state) => state.allData,
}
export const mutations = {
  GET_DATAS(state, payload) {
    state.allData = payload
  },
  UPDATE_DATA(state, payload) {
    const item = state.allData[payload.index]
    Object.assign(item, payload)
  },
}
export const actions = {
  getDatas({ commit, state }, payload) {
    return fetch(`URL_FETCH`)
      .then((data) => data.json())
      .then((data) => {
        commit('GET_DATAS', data)
      })
      .catch((err) => console.log(err))
  },
  updateData({ commit, state }, payload) {
    commit('UPDATE_DATA', payload)
  },
}

in /layouts/default.vue

beforeCreate() {
  this.$store.dispatch('getDatas').then(() => {
    connectSocket()
  })
},
methods: {
  connectSocket() {
    // connect & received message from socket
    // received message from socket
    this.$root.$emit('updateData', {
      index: 12,
      price: 34,
      change: 56,
      percent: 78,
    })
  },
},

and in /pages/blog/index.vue

<template>
  <div>
    <div
      v-for="index in getAllData"
      :key="index.name"
      class="w-100 grid-wrapper"
    >
      <div>{{ index.price }}</div>
      <div>{{ index.change }}</div>
      <div>{{ index.percent }}</div>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  data() {
    return {}
  },
  computed: {
    ...mapGetters(['getAllData']),
  },
  mounted() {
    this.$root.$on('updateData', (item) => {
      this.$store.dispatch('updateData', {
        index: item.index,
        price: item.price,
        percent: item.percent,
        change: item.change,
      })
    })
  },
}
</script>

Upvotes: 2

Views: 1479

Answers (1)

kissu
kissu

Reputation: 46676

Here is a complete example on how to use Vuex and load the data efficiently into a Nuxt app (subjective but using good practices).

/pages/index.vue

<template>
  <div>
    <main v-if="!$fetchState.pending">
      <div v-for="user in allData" :key="user.id" style="padding: 0.5rem 0">
        <span>{{ user.email }}</span>
      </div>
    </main>
    <button @click="fakeUpdate">Update the 2nd user</button>
  </div>
</template>

<script>
import { mapState, mapActions } from 'vuex'

export default {
  data() {
    return {
      mockedData: {
        name: 'John Doe',
        username: 'jodoe',
        email: '[email protected]',
        phone: '1-770-736-8031 x56442',
        website: 'hildegard.org',
      },
    }
  },
  async fetch() {
    await this.setAllData()
  },
  computed: {
    ...mapState(['allData']),
  },
  methods: {
    ...mapActions(['setAllData', 'updateData']),

    fakeUpdate() {
      this.updateData({ index: 1, payload: this.mockedData })
    },
  },
}
</script>

/store/index.js

import Vue from 'vue'

export const state = () => ({
  allData: [],
})

export const mutations = {
  SET_ALL_DATA(state, payload) {
    state.allData = payload
  },
  UPDATE_SPECIFIC_DATA(state, { index, payload }) {
    Vue.set(state.allData, index, payload)
  },
}

export const actions = {
  async setAllData({ commit }) {
    try {
      const httpCall = await fetch('https://jsonplaceholder.typicode.com/users')
      const response = await httpCall.json()
      commit('SET_ALL_DATA', response)
    } catch (e) {
      console.warn('error >>', e)
    }
  },
  updateData({ commit }, { index, payload }) {
    commit('UPDATE_SPECIFIC_DATA', { index, payload })
  },
}

Upvotes: 2

Related Questions