17 votes

Rendu par Vuex des données extraites de l'API REST

Pour un tel composant

<template>
    <div>
        <router-link :to="{name:'section', params: { sectionId: firstSectionId }}">Start</router-link>
    </div>
</template>

<script lang="ts">
    import { mapActions } from "vuex"

    export default {
        mounted() {
            this.getSectionId()
        },
        computed: {
            firstSectionId() {
                return this.$store.state.firstSectionId
            }
        },
        methods: mapActions(["getSectionId"])
    }
</script>

Magasin :

const store: any = new Vuex.Store({
    state: {
        firstSectionId: null
    },
    // actions,
    // mutations
})

J'ai une requête web dans le getSectionId et il récupère les données de manière asynchrone et appelle une mutation qui remplira les données de la base de données. firstSectionId en state . Lors du rendu initial firstSectionId es null et j'obtiens l'avertissement qu'un paramètre requis est manquant lors du rendu de router-link .

Il n'y a pas de problème pour ajouter v-if="firstSectionId" . Mais en général, quelle est l'approche pour récupérer les données d'un serveur afin de les afficher ? Actuellement, tous mes composants vérifient s'il y a des données présentes dans le magasin avant d'effectuer le rendu, est-ce normal ou y a-t-il une meilleure façon d'attendre que les données soient chargées avant d'effectuer le rendu ?

50voto

t_dom93 Points 2396

Pour récupérer des données de manière asynchrone, il est possible d'utiliser l'option promesse dans le magasin vuex actions .

Vue.http.get(API_URL)
.then((response) => {
  //use response object     
})
.catch((error => {
    console.log(error.statusText)
}))

Pour le démontrer, je demande cette route . Vous pouvez voir à quoi devrait ressembler la réponse. Sauvegardons l'objet réponse dans le tableau state.users.

store.js

const store = new Vuex.Store({
  state: {
    users: []
  },  
  mutations: {
    FETCH_USERS(state, users) {
        state.users = users
    }
  },
  actions: {
    fetchUsers({ commit }, { self })  {         
        Vue.http.get("https://jsonplaceholder.typicode.com/users")
        .then((response) => {
            commit("FETCH_USERS", response.body);
            self.filterUsers(); 
        })
        .catch((error => {
            console.log(error.statusText)
        }))
    }
  }
})

export default store

Vous avez remarqué qu'il y a self.filteruser() après la validation. C'est un moment crucial. Avant cela, nous sommes commettre une mutation qui est une opération synchrone et nous sommes sûrs que nous aurons notre réponse dans store.state qui peut être utilisée en filterUsers() méthode (n'oubliez pas de passer le self parm)

Utilisateurs.vue

import store from "../store/store"

export default {
  name: 'users',
  created() {
    this.$store.dispatch("fetchUsers", { self: this })       
  },
  methods:{
    filterUsers() {
      //do something with users
       console.log("Users--->",this.$store.state.users)       
    }
  }
}

De meilleurs moyens (ES6 & ES7)

ES6 Promesses pour la programmation asynchrone

//User.vue
created() {
  this.$store.dispatch("fetchUser").then(() => {
    console.log("This would be printed after dispatch!!")
   })
}

//store.js
actions: {
    fetchUser({ commit }) {
        return new Promise((resolve, reject) => {
            Vue.http.get("https://jsonplaceholder.typicode.com/users")
            .then((response) => {
                commit("FETCH_USERS", response.body);
                resolve();
            })
            .catch((error => {
                console.log(error.statusText);
            }));
        });
    }
}

ES7 : async/await

Pour éviter l'enfer des callbacks et améliorer la programmation asynchrone, utilisez async et vous pouvez await sur une promesse. Le code semble beaucoup plus facile à suivre (comme il est synchrone), mais le code n'est pas lisible pour les navigateurs, vous aurez donc besoin du transpilateur Babel pour l'exécuter.

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // wait for actionA to finish
    commit('gotOtherData', await getOtherData())
  }
}

1voto

mzgajner Points 1084

D'après mon expérience, vous pouvez sauter quelques vérifications si vous prédéfinissez l'état avec une valeur vide du même type que le résultat attendu (si vous savez à quoi vous attendre, bien sûr), par exemple, si vous avez un tableau d'éléments, commencez par [] au lieu de null car il ne se casse pas v-for Les directives de l'Union européenne, .length et des tentatives similaires d'accès aux données.

Mais d'une manière générale, l'ajout de v-if est une chose tout à fait normale. Il y a une section à ce sujet dans le vue-router la documentation et la vérification de l'existence ou non de propriétés est exactement ce qu'elle suggère. Une autre solution possible mentionnée est de récupérer les données à l'intérieur de beforeRouteEnter ce qui garantit que vous accéderez toujours au composant dont les données sont déjà disponibles.

En fin de compte, les deux solutions sont correctes, et la décision entre les deux est plus une question d'UX/UI.

0voto

Nathan Agersea Points 129

J'avais des besoins similaires pour les emplacements et l'api google map. J'avais besoin de récupérer mes emplacements depuis l'API, de les charger dans une liste, puis de les utiliser dans un composant de carte pour créer les marqueurs. J'ai récupéré les données dans une action Vuex avec axios, je les ai chargées dans mon état avec une mutation, puis j'ai utilisé un getter pour récupérer le tableau résultant dans le hook du cycle de vie mounted. Cela a donné un tableau vide car mounted s'est déclenché avant que l'action asynchrone ne soit résolue.

J'ai utilisé store.subscribe pour résoudre le problème de cette manière :

<template>
  <div class="google-map" :id="mapName"></div>
</template>

<script>
import GoogleMapsLoader from 'google-maps';
import { mapGetters } from 'vuex';

export default {
  name: 'google-map',
  props: ['name'],
  computed: {
    ...mapGetters({
      locations: 'locations/locations',
    }),
  },
  data() {
    return {
      mapName: `${this.name}-map`,
    };
  },
  mounted() {
    this.$store.subscribe((mutation, state) => {      
      if (mutation.type === 'locations/SAVE_LOCATIONS') {
        GoogleMapsLoader.KEY = 'myKey';
        GoogleMapsLoader.load((google) => {
          /* eslint-disable no-new */
          const map = new google.maps.Map(document.getElementById('locations-map'));

          // loop through locations and add markers to map and set map boundaries
          const bounds = new google.maps.LatLngBounds();

          // I access the resulting locations array via state.module.property
          state.locations.locations.forEach((location) => {
            new google.maps.Marker({
              position: {
                lat: location.latitude,
                lng: location.longitude,
              },
              map,
            });
            bounds.extend({
              lat: location.latitude,
              lng: location.longitude,
            });
          });

          map.fitBounds(bounds);
        });
      }
    });
  },
};

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X