web-dev-qa-db-fra.com

Comment utiliser Vuex Getter après un appel API asynchrone

Quelle est la bonne façon d'appeler un getter in vuex après avoir envoyé une action asynchrone qui a muté l'état?

J'ai créé un exemple d'extrait pour illustrer ce que je veux dire. Comme vous pouvez le voir, getLastNameByName() échoue car state.persons Est vide. La chose étrange est que si j'imprime state.persons Dans cet getter, il imprime le tableau après appel api.

Le comportement attendu est que getLastNameByName('John') renvoie {name: 'John', lastname: 'Smith'}

const store = new Vuex.Store({
  state: {
    persons: []
  },

  getters: {
    getLastNameByName: (state) => (name) => {

      // console.log(state.persons) returns the state, yet I cannot call .find on it 
      return state.persons.find(element => {
        return element.name === name
      }).lastname
    },
  },

  mutations: {
    setPersons: (state, payload) => {
      state.persons = [...payload]
    }
  },

  actions: {
    async getPeople({commit}) {
        return new Promise(function(resolve, reject) {
          setTimeout(async () => {
             commit('setPersons', [{
               name: 'John',
               lastname: 'Smith'
            }, {
            name: 'Sarah',
            account: 'Appleseed'
          }])

           resolve();
         }, 1000)
      })
  }
  }
})

new Vue({
  store,
  el: '#app',
  mounted() {
    this.$store.dispatch('getPeople').then( () =>  { 
      console.log(this.$store.getters.getLastNameByName('John'))
    })
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>

<div id="app">
</div>
7
Frank

setTimeout () ne renvoie pas d'objet attendu. Vérifiez avec promesse:

const store = new Vuex.Store({
  state: {
    persons: []
  },

  getters: {
    getLastNameByName: (state) => (name) => {
      return state.persons.find(element => {
        return element.name === name
      }).lastname
    },
  },

  mutations: {
    setPersons: (state, payload) => {
      state.persons = [...payload]
    }
  },

  actions: {
    async getPeople({commit}) {
        return new Promise(function(resolve, reject) {
          setTimeout(async () => {
             commit('setPersons', [{
               name: 'John',
               lastname: 'Smith'
            }, {
            name: 'Sarah',
            account: 'Appleseed'
          }])

           resolve();
         }, 1000)
      })
    }
  }
})

new Vue({
  store,
  el: '#app',
  mounted() {
    this.$store.dispatch('getPeople').then(() => {
       console.log(this.$store.getters.getLastNameByName('John'));
    })
  } 
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>

<div id="app">
</div>

Quoi qu'il en soit, la gestion directe des appels asynchrones à stocker n'est pas une bonne façon. Je pense que dans ce cas, la meilleure solution consiste à watch stocker l'état ou utiliser computed propriétés.

3
YD1m

Essayé sur jsbin.com avec quelques améliorations et aucun problème:

<!DOCTYPE html>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Vue example</title>

<div id="app">
  <show-person
    :name="getLastName('John')"
  ></show-person>
</div>

<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vuex.min.js"></script>

<script>
const store = new Vuex.Store({
  state: {
    persons: []
  },

  getters: {
    getLastName: state => name => {
      return state.persons.length
        ? state.persons.find(element => {
            return element.name === name
          }).lastname
        : ''
    }
  },

  mutations: {
    setPersons: (state, payload) => {
      state.persons = [...payload]
    }
  },

  actions: {
    getPeople: ({ commit }) => new Promise(res => {
      const data = [
        {name: 'John', lastname: 'Smith'},
        {name: 'Sarah', account: 'Appleseed'}
      ]
      setTimeout(() => {
        commit('setPersons', data)
        res()
      }, 1000)
    })
  }
})

const ShowPerson = {
  functional: true,
  render: (h, ctx) => h('p', ctx.props.name)
}

new Vue({
  store,
  el: '#app',

  components: {
    ShowPerson
  },

  computed: {
    ...Vuex.mapGetters([
      'getLastName'
    ])
  },

  methods: {
    ...Vuex.mapActions([
      'getPeople'
    ])
  },

  created () {
    this.getPeople()
  }
})
</script>
0
Vladislav Ladicky