web-dev-qa-db-fra.com

Puis-je appeler commit à partir d'une des mutations dans le magasin Vuex

J'ai un vuex store, comme suit:

import spreeApi from '../../gateways/spree-api'
// initial state
const state = {
  products: [],
  categories: []
}

// mutations
const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   commit('SET_CATEGORIES')
 },
 SET_CATEGORIES: (state) => {
   state.categories = state.products.map(function(product) { return product.category})
 }

}

const actions = {
 FETCH_PRODUCTS: (state, filters) => {
   return spreeApi.get('products').then(response => state.commit('SET_PRODUCTS', response))
 }
}

export default {
  state,
  mutations,
  actions
}

Je veux appeler mutation: SET_CATEGORIES à partir de mutation: SET_PRODUCTS, mais cela me donne une erreur: 

projectFilter.js: 22 Uncaught (promis) ReferenceError: commit n'est pas défini (…)

Quelle devrait être la bonne façon de faire cela? J'ai essayé store.commit et this.commit, mais ceux-ci ont également donné des erreurs similaires.

36
Saurabh

Lorsque vous effectuez déjà une mutation, il n’ya aucun moyen de commit une autre mutation. Une mutation est un appel synchrone qui modifie l'état. Dans une mutation, vous ne pourrez pas commettre une autre mutation.

Voici la référence de l'API pour Vuex: https://vuex.vuejs.org/fr/api.html

Comme vous pouvez le constater, un gestionnaire de mutation ne reçoit que state et payload, rien de plus. Par conséquent, vous obtenez commit comme undefined.

Dans votre cas ci-dessus, vous pouvez définir le PRODUIT et les CATÉGORIES dans le même gestionnaire de mutation comme un commit unique. Vous pouvez essayer si le code suivant fonctionne:

// mutations
const mutations = {
    SET_PRODUCTS_AND_CATEGORIES: (state, response) => {
        state.products = response.data.products
        state.categories = state.products.map(function(product) { return product.category})
    },
    // ...
}

EDIT: Veuillez vous reporter à la réponse ci-dessous, fournie par Daniel S. Deboer. La méthode correcte consiste à commettre deux mutations à partir d'une seule action, comme décrit dans sa réponse.

24
Mani

Si vous devez absolument commettre deux mutations, pourquoi ne pas le faire à partir d'une action? Les actions ne doivent pas nécessairement exécuter des opérations asynchrones. Vous pouvez déstructurer la méthode commit dans votre action de la même manière que vous le faites avec state, comme ceci:

commitTwoThings: ({commit}, payload) => {
  commit('MUTATION_1', payload.thing)
  commit('MUTATION_2', payload.otherThing)
}
52
Daniel S. Deboer

Pour partager du code entre des mutations, vous devez créer une nouvelle fonction qui effectue le travail, que vous pouvez ensuite réutiliser. Heureusement, les mutations sont simplement d'anciennes fonctions, et nous pouvons transmettre le paramètre state comme bon nous semble, ce qui est très facile à faire.

Par exemple:

const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   setCategories(state)
 },
 SET_CATEGORIES: (state) => {
   setCategories(state)
 }
}

function setCategories(state) {
  state.categories = state.products.map(product => product.category)
}
17
Daniel Buckmaster

Et si j'ai un code commun qui affecte l'état entre plusieurs mutations, je dois dupliquer le même code sur toutes mes mutations? Ou il y a une meilleure façon de faire ça?

8
Nacho

En lisant la documentation de Vuex sur Actions , il est clair en quoi elles sont faites.

  • commettre des mutations au lieu de muter l'état
  • peut contenir des opérations asynchrones arbitraires

Les actions can (et non must) contiennent du code asynchrone. En fait, l'exemple suivant est correct 

increment (context) {
   context.commit('increment')
}

Je ne vois aucun problème à utiliser des actions pour effectuer plusieurs mutations.

3
Wanny Miarelli

Dans votre cas, vous ne devriez envisager qu'une seule mutation, à savoir SET_PRODUCTS.

// mutations
const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   state.categories = state.products.map(function(product) { return product.category})
 }
}

Vous ne devriez jamais avoir besoin d'appeler SET_CATEGORIES séparément. Penses-y! Les catégories ne peuvent muter que si les produits sont modifiés. Et les produits ne peuvent changer que via SET_PRODUCTS.

2
jiv-e

Edit: Je suis tombé sur un problème très similaire et la solution pour moi consistait à utiliser un getter vuex: https://vuex.vuejs.org/en/getters.html
Vos catégories sont en fait une version "calculée" de vos produits. Avoir des catégories en tant que getter vous permet de les synchroniser avec les produits et d’éviter la duplication des données de votre magasin.

Afin de répondre à la question dans le titre, je laisse ma réponse initiale.
Une alternative à la solution Daniel Buckmaster:

const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   this.SET_CATEGORIES(state)
 },
 SET_CATEGORIES: (state) => {
   state.categories = state.products.map(product => product.category)
 }
}

Comme vous pouvez le constater, vous pouvez appeler directement la mutation elle-même. (comme l'a dit Daniel, ce sont des fonctions simples après tout)
Je pense qu’il s’agit d’une réponse plus appropriée à la question initiale: c’est un moyen réel de composer des mutations sans duplication de code ni fonctions supplémentaires.

0
Guillaume Meral

Pour le compte rendu. Pour appeler d'autres mutations à partir d'une méthode de mutation, procédez comme suit:

const mutations = {
    mutationOne(state, payload){
        this.commit("mutationTwo", payload)
    },
    mutationTwo(state, payload){
        console.log("called form another mutation", payload)
    }
}
0
Kubwimana Adrien
import spreeApi from '../../gateways/spree-api'
// initial state
const state = {
  products: [],
  categories: []
}

// mutations
const mutations = {
 SET_PRODUCTS: (state, {response,commit}) => { // here you destructure the object passed to the mutation to get the response and also the commit function
   state.products = response.data.products
   commit('SET_CATEGORIES') // now the commit function is available
 },
 SET_CATEGORIES: (state) => {
   state.categories = state.products.map(function(product) { return product.category})
 }

}

const actions = {
 FETCH_PRODUCTS: ({commit}, filters) => { // here you destructure the state to get the commit function
   return spreeApi.get('products').then(response => commit('SET_PRODUCTS', {response,commit})) // here you pass the commit function through an object to 'SET_PRODUCTS' mutation
 }
}

export default {
  state,
  mutations,
  actions
}

Cela devrait résoudre le problème. Vous pouvez injecter le commit dans votre mutation à partir de l'action afin de pouvoir vous engager à partir de votre mutation. J'espère que cela t'aides

0
Andrei