
Dans le chapitre précédent, nous avons appris à définir notre espace de stockage de données dans Vuex avec state et des accesseurs, mais également à récupérer nos données avec des méthodes comme mapState et mapGetters. Dans ce chapitre, nous allons passer à l'étape suivante dans la gestion de notre data store : mettre à jour et modifier nos données dans Vuex avec les mutations.
Comme nous l'avons vu dans la structure initiale de store.js :
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
}
}) La deuxième clé définie dans le store est appelée mutation. Comme son nom l'indique, elle contiendra un objet de toutes les propriétés responsables de modifications du state.
Si nous prenons l'exemple traditionnel du compteur, nous pouvons définir une mutation comme suit :
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
INCREASE_COUNT(state) {
state.count += 1
}
},
actions: {
}
})Par défaut, la mutation reçoit le state en premier argument. Ce dernier peut ensuite être utilisé pour effectuer les modifications nécessaires. Dans l'exemple ci-dessus, nous ne faisons qu'incrémenter state.count de 1.
Cependant, cette technique a ses limites car nous pourrions envisager de rendre la valeur incrémentée dynamique. Heureusement, les mutations peuvent prendre un deuxième argument : le paramètre payload.
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
INCREASE_COUNT(state, payload) {
state.count += Number(payload)
}
},
actions: {
}
})Dans l'exemple révisé, nous autorisons maintenant l'utilisateur à :
transmettre de combien nous voulons que state.count soit incrémentée ;
fixer une valeur par défaut de 1, au cas où l'utilisateur oublie de transmettre une valeur :
convertir la valeur en nombre pour qu'elle ne s'ajoute pas comme chaîne de caractères par accident.
Lorsqu'il s'agit de modifier le state d'une application, il est essentiel que cela soit fait au bon moment. En d'autres termes, une attention et un soin particuliers sont accordés à ces événements, afin de s'assurer que toute erreur soit facilement identifiée. Par conséquent, plutôt que d'invoquer la mutation comme une fonction normale, nous utilisons une action spéciale : commit.
Lorsqu'une mutation est actée, l'action commit prend deux paramètres :
nom de la mutation ;
payload (facultatif).
this.$store.commit('INCREMENT_COUNT', 2)Lorsqu'une mutation est actée, la modification est faite immédiatement. En d'autres termes, les mutations Vuex sont synchrones, ce qui signifie qu'il n'est par exemple pas possible de récupérer des données d'une API dans une mutation.
Bien sûr, vous vous demandez probablement de quelle manière il faudrait acter les mutations. Pour répondre à cette question, nous allons devoir parler des actions.
Jusqu'à présent, nous avons appris que :
State contient notre data store global ;
les accesseurs (getters) servent de propriétés calculées à nos espaces de stockage de données ;
les mutations nous permettent de mettre à jour/modifier le state.
La dernière pièce du puzzle est les actions. Elles nous servent à coordonner la logique derrière les mutations. En d'autres termes, elles sont similaires à la propriété methods dans une instance de Vue.

Pour continuer avec notre exemple de compteur, nous pourrions définir notre action de la façon suivante :
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
INCREASE_COUNT(state, amount = 1) {
state.count += Number(amount)
}
},
actions: {
incrementCount(context, amount) {
context.commit('INCREMENT_COUNT', amount)
}
}
}) Une action est composée d'un nom, du paramètre context et d'un payload optionnel (comme pour les mutations). Dans l'exemple ci-dessus :
incrementCount est le nom de l'action ;
le paramètre context nous donne accès aux mêmes méthodes et propriétés dans l'instance du store Vuex (par exemple, commit, state, getters, etc.) ;
amount est le payload que nous devons transmettre à la mutation, afin qu'elle augmente de la bonne valeur.
Sur la base de l'exemple ci-dessus, vous vous demandez peut-être pourquoi ne pas simplement appeler la mutation directement. Mais que se passerait-il lorsqu'il faut diminuer le count d'une certaine valeur ?
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
INCREASE_COUNT(state, amount = 1) {
state.count += Number(amount)
},
DECREASE_COUNT(state, amount = 1) {
state.count -= Number(amount)
}
},
actions: {
incrementCount(context, amount) {
context.commit('INCREMENT_COUNT', amount)
}
}
})Une certaine logique est nécessaire pour déterminer quand déclencher chaque mutation. Pour tenir compte de cela, rendons notre action générique de la manière suivante :
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
INCREASE_COUNT(state, amount = 1) {
state.count += Number(amount)
},
DECREASE_COUNT(state, amount = 1) {
state.count -= Number(amount)
}
},
actions: {
updateCount(context, amount) {
if (amount >= 0) {
context.commit('INCREASE_COUNT', amount)
} else {
context.commit('DECREASE_COUNT', amount)
}
}
}
})Pourquoi ne pas simplement réunir les mutations en une seule ?
Nous pourrions rendre la mutation générique à quelque chose comme CHANGE_COUNT, mais en regardant l'historique, cela nous fournirait moins de détails, ce qui rend les choses plus difficiles à débugger. Par conséquent, il est préférable que les mutations gardent un seul usage. Laissez la logique aux actions.
De plus, comme le paramètre context peut être utilisé pour accéder à de nombreuses propriétés différentes du store Vuex, vous allez découvrir une technique de codage courante qui est l'affectation par décomposition (desctructuring, en anglais). Elle est faite pour simplifier le code et le rendre plus facile à lire. Avec notre exemple, cela ressemblerait à ce qui suit :
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
INCREASE_COUNT(state, amount = 1) {
state.count += Number(amount)
},
DECREASE_COUNT(state, amount = 1) {
state.count -= Number(amount)
}
},
actions: {
updateCount({ commit }, amount) {
if (amount >= 0) {
commit('INCREASE_COUNT', amount)
} else {
commit('DECREASE_COUNT', amount)
}
}
}
})En plus de nous donner la liberté de déterminer la logique du moment où les mutations sont déclenchées, les actions sont asynchrones. Cela signifie que vous ne pouvez appeler des API et acter des mutations qu'en cas de réussite. Ou en fonction de ce que vous souhaitez réaliser.
Maintenant que nous savons comment définir les actions, la question suivante se pose : comment les utiliser dans nos composants ?
Tout comme il existe un terme spécial pour invoquer les mutations (c'est-à-dire acter ou « commit »), il existe aussi un terme spécial pour les actions : propager (dispatch). En d'autres termes, vous envoyez l'action pour exécuter une tâche. Donc, si vous vouliez propager une action depuis votre composant, cela ressemblerait à ce qui suit :
<template>
<div>
<p>{{ count }}</p>
<button @click="sendUpdateCountAction">Increment</button>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['count'])
},
methods: {
sendUpdateCountAction() {
this.$store.dispatch('updateCount')
}
}
}
</script>Cependant, comme pour mapState et mapGetters, il existe un équivalent pour les actions : mapActions
<template>
<div>
<p>{{ count }}</p>
<button @click="updateCount">Increment</button>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapActions(['updateCount'])
}
}
</script>Vous connaissez maintenant les principaux éléments de Vuex !
Vous trouverez le code source des exercices dans le repo GitHub du cours, dans le dossier cafe-with-a-view. Pour commencer, consultez la branche P4C4-Begin.
Migrez l'événement d'ajout au panier de Home.vue et MenuItem.vue vers Vuex.
Créez une mutation qui met à jour le state du shoppingCart.
Créez une action qui peut être déclenchée depuis MenuItem.vue qui met à jour le panier d'achat dans Home.vue
Dans ce chapitre, vous avez découvert :
ce que sont les mutations et les actions ;
comment utiliser les mutations pour mettre à jour le state ;
comment utiliser les actions pour gérer vos mutations ;
comment déclarer et utiliser des actions dans vos composants.
C'est maintenant le moment de passer en revue nos acquis de la partie. Rendez-vous au prochain chapitre !