Kann ich Commit von einer der Mutationen im Vuex Store aufrufen?


76

Ich habe einen Vuex- Shop wie folgt:

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
}

Ich möchte Mutation aufrufen: SET_CATEGORIESvon Mutation : SET_PRODUCTS, Aber das gibt mir Fehler:

projectFilter.js: 22 Nicht gefangen (im Versprechen) ReferenceError: Commit ist nicht definiert (…)

Was sollte der richtige Weg sein, um dies zu tun. Ich habe es versucht store.commitund this.commit, aber diese gaben auch ähnliche Fehler.


1
Verwandte (geschlossene) Ausgabe: github.com/vuejs/vuex/issues/907
Amir Ali Akbari

Hallo @Saurabh, ich habe Kubiwama Adriens Antwort getestet, und es scheint, dass es das hat, was Sie brauchen. Vielleicht testen Sie es und aktualisieren Sie dieses Forum mit der neuesten Antwort. Vielen Dank!
Irfandy Jip

Warum nicht eine Aktion verwenden und mehrere Mutationen darin aufrufen?
August

Antworten:


45

Wenn Sie bereits eine Mutation durchführen, gibt es keine Möglichkeit zu einer commitanderen Mutation. Eine Mutation ist ein synchroner Aufruf, der den Status ändert. Innerhalb einer Mutation können Sie keine weitere Mutation festlegen.

Hier ist die API-Referenz für Vuex: https://vuex.vuejs.org/en/api.html

Wie Sie sehen können, erhält ein Mutationshandler nur stateund payloadnichts weiter. Deshalb bekommen Sie commitals undefined.

In Ihrem obigen Fall können Sie PRODUKT und KATEGORIEN als Teil desselben Mutationshandlers als einzelnes Commit festlegen. Sie können versuchen, ob der folgende Code funktioniert:

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

BEARBEITEN: Bitte beziehen Sie sich auf die Antwort von Daniel S. Deboer . Die richtige Methode besteht darin, zwei Mutationen aus einer einzigen Aktion zu begehen, wie in seiner Antwort beschrieben.


6
Ich frage mich, warum es nicht erlaubt ist, eine Mutation aus einer anderen Mutation zu begehen. Einige Mutationen können so atomar sein, dass sie von größeren Mutationsfunktionen verwendet werden sollten. In der aktuellen Version sollte aufgrund dieser Einschränkung viel Code dupliziert werden.
Asqan

Ja, es ist möglich, ein Commit von einer anderen Mutation ohne Nebenwirkungen durchzuführen. Das Debuggen einer falschen Statusänderung, wenn sich Mutationen gegenseitig aufrufen, führt jedoch zu einer sehr schlechten Entwicklererfahrung. Ich denke, deshalb wird es nicht empfohlen oder erlaubt. Aber zum Glück haben wir diese einfache Lösung - wir können immer eine einzelne Mutation definieren, die mehrere Zustandsänderungen durchführt, wie in dieser Frage / Antwort gezeigt.
Mani

IMHO, die Antwort ist unten (zwei Dinge aus einer Aktion begehen). Es ist sauberer und lesbarer.
JCKödel

@ JCKödel Ich stimme zu, die richtige Antwort sollte die folgende sein (zwei Mutationen aus der Aktion begehen), die von Daniel bereitgestellt wird. Ich werde in meine Antwort oben eine Notiz einfügen, um auf die bessere Antwort unten zu verweisen.
Mani

@Mani warum wird dies nicht Danis Antwort vorgezogen? Ich dachte, dies sei vom Vuex-Standpunkt aus semantisch korrekter, da die Aktion hauptsächlich für asynchrone Operationen konzipiert ist.
Alex Napitupulu

109

Wenn Sie unbedingt zwei Mutationen begehen müssen, warum nicht aus einer Aktion heraus? Aktionen müssen keine asynchronen Vorgänge ausführen. Sie können die Festschreibungsmethode in Ihrer Aktion genauso zerstören wie mit dem Status wie folgt:

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

4
Ja, das sollte die Antwort sein.
Thiago Yoithi

3
Warum nicht? .. Mit vue-native-websocke müssen Sie Daten von einem ws-Server in einer Mutation (SOCKET_ONMESSAGE) verarbeiten ... Und es gibt auch keine Möglichkeit, eine Aktion aus einer Mutation aufzurufen.
Constantin De La Roche

52

Für die Aufzeichnung. Um andere Mutationen aus einer Mutationsmethode aufzurufen, gehen Sie folgendermaßen vor:

const mutations = {
    mutationOne(state, payload){
        this.commit("mutationTwo", payload)
    },
    mutationTwo(state, payload){
        console.log("called from another mutation", payload)
    }
}

6
Ist dies eine neue (ish) Funktion von Vuex? Überrascht, dass es zweieinhalb Jahre gedauert hat, bis jemand die klar richtige Antwort gefunden hat.
Michael Hays

3
Ich bin mir nicht ganz sicher, ob dies als Best Practice angesehen wird. Diese Antwort hat die Frage jedoch direkt beantwortet. ohne Alternativen bereitzustellen, die von anderen über vorgeschlagen wurden action. Ich habe es getestet und es scheint gut zu funktionieren. Ich denke, diese Antwort sollte stattdessen oben stehen. Diese Lösung wurde bereits in Vuex Github besprochen. Bitte fügen Sie die Möglichkeit hinzu, eine Mutation aus einer anderen Mutation # 907 aufzurufen, wenn jemand mehr Beweise lesen möchte .
Irfandy Jip

11
Übrigens, wenn Sie einen modulesNamespace haben, obwohl Sie sich unter derselben Datei befinden, müssen Sie this.commit('modulesName/mutationName')nur für den Fall darauf zugreifen, wenn sich jemand wundert. Wenn Sie weitere Informationen benötigen, ist es immer eine gute Erinnerung, nur console.log(this)innerhalb der Mutation zu arbeiten. Es scheint, dass sie dieselbe Instanz enthält wie Vue, auf die Sie auch $routevon dort aus zugreifen können.
Irfandy Jip

27

Um Code zwischen Mutationen zu teilen, müssen Sie eine neue Funktion erstellen, die die Arbeit ausführt und die Sie dann wiederverwenden können. Glücklicherweise sind Mutationen nur alte Funktionen, und wir können den stateParameter nach Belieben weitergeben, so dass dies recht einfach ist.

Zum Beispiel:

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)
}

Ich denke, dies gibt einen Fehler über den Verkettungszustand außerhalb einer Mutation
Drew Baker

Was bringt dich dazu, das zu denken? Solange Sie nur setCategoriesinnerhalb einer Mutation anrufen , ist dies in Ordnung.
Daniel Buckmaster

11

Und wenn ich einen gemeinsamen Code habe, der den Status zwischen mehreren Mutationen beeinflusst, muss ich für alle meine Mutationen denselben Code duplizieren? Oder gibt es einen besseren Weg, das zu tun?


8
Die Stille ist ohrenbetäubend. Ich würde auch gerne eine Antwort darauf finden.
Daninthemix

1
@Nacho siehe die Antwort, die ich gerade erstellt habe. Es ist nicht ideal, aber es ist das Beste, das ich kenne
Daniel Buckmaster

Ich stimme Daniel zu. Funktionen sind der Weg, um Code wiederzuverwenden.
Cstricklan

1
Ja - eine Mutation ist nur eine Funktion, die den Zustand als Argument nimmt und ihn modifiziert. Sie können beliebig viele Hilfsfunktionen deklarieren und wiederverwenden.
Chris Williamson

1
Bitte posten Sie eine Antwort nur, wenn Sie eine Antwort haben. Wenn Sie etwas fragen möchten, erstellen Sie einen neuen Thread.
Pablo

5

Wenn Sie die Vuex-Dokumentation zu Aktionen lesen , ist klar, wofür sie gemacht sind.

  • Mutationen begehen, anstatt den Staat zu mutieren
  • kann beliebige asynchrone Operationen enthalten

Aktionen können ( müssen nicht ) asynchronen Code enthalten. In der Tat ist das folgende Beispiel richtig

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

Ich sehe kein Problem darin, Aktionen zum Durchführen mehrerer Mutationen zu verwenden.


3

In Ihrem Fall sollten Sie nur eine Mutation in Betracht ziehen, nämlich SET_PRODUCTS.

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

Sie sollten SET_CATEGORIES niemals separat aufrufen müssen. Denk darüber nach! Kategorien können nur mutieren, wenn Produkte geändert werden. Und Produkte können sich nur über SET_PRODUCTS ändern.


1

Bearbeiten: Ich bin auf ein sehr ähnliches Problem gestoßen und die Lösung für mich bestand darin, einen vuex getter zu verwenden: https://vuex.vuejs.org/en/getters.html
Ihre Kategorien sind tatsächlich eine "berechnete" Version Ihrer Produkte. Wenn Sie Kategorien als Getter haben, können Sie sie mit Produkten synchronisieren und vermeiden, dass die Daten in Ihrem Geschäft dupliziert werden.

Um die Frage im Titel zu beantworten, lasse ich meine ursprüngliche Antwort.
Eine Alternative zur Daniel Buckmaster-Lösung:

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)
 }
}

Wie Sie sehen, können Sie die Mutation selbst direkt aufrufen. (wie Daniel sagte, es sind schließlich nur einfache Funktionen)
Ich glaube, dass dies eine angemessenere Antwort auf die ursprüngliche Frage ist: Es ist eine tatsächliche Art, Mutationen ohne Codeduplizierung oder zusätzliche Funktionen zusammenzusetzen


Das Aufrufen einer Mutationsfunktion ist nicht dasselbe wie das Festschreiben einer Mutation.
Emile Bergeron

Könnten Sie näher darauf eingehen? Ist das wirklich ein Problem, da wir es von einer anderen Mutation nennen?
Guillaume Meral

Beispielsweise wird der Handler für registrierte Plugins nicht für die zweite Mutation aufgerufen. Dies bedeutet, dass die Vue-Entwicklungstools nicht die zweite Mutation in der Liste der Mutationen anzeigen, zu denen Sie "Zeitreisen" können.
Emile Bergeron

Der Anwendungsfall ist jedoch eine einzelne Mutation, die zwei Dinge bewirkt. Ich verstehe Ihren Standpunkt, aber in diesem Fall möchten Sie keinen Geschäftsstatus mit desynchronisierten Listen von Produkten und Kategorien haben.
Guillaume Meral

1
Hm ja, das habe ich mit "Eine Alternative zur Daniel Buckmaster-Lösung" gemeint. Es ist meine erste StackOverflow-Antwort, vielleicht hätte ich stattdessen seine Lösung kommentieren sollen.
Guillaume Meral

1

Weisen Sie zunächst einer Variablen die Schaltfläche Vue zu: In main.js:

  export const app = new Vue({  
  router,
  vuetify,
  store,....

Importieren Sie dann die Variable "app" in die js-Datei, in der Sie die Mutation definieren: In modules.js:

import { app } from "../../main";

Sie können es jetzt als "app. $ Store.commit" verwenden:

mutations: {
[AUTH_SET_TOKEN]: () => {
app.$store.commit(USER_SUCCESS, params );
},...

0

Ich rufe lieber an als mutations.SET_CATEGORIES(state): - 2 verschiedene Commits aus einer künstlichen Aktion aufzurufen - oder commit()innerhalb einer Mutation zu tun, da dies das Testen von Einheiten schwieriger macht.

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

Meiner Meinung nach müssen Sie nicht SET_CATEGORIESin der VueToolbox sehen. Die Zeitreise sollte sowieso funktionieren. Bitte korrigiere mich wenn ich falsch liege.


0

Ich denke

Das Aufrufen einer Mutation von einer anderen Mutation ist eine schlechte Idee, da der Status und die Komponenten schwer zu debuggen sind

const mutations = {
    mutationOne(state, payload){
        this.commit("mutationTwo", payload)
    },
    mutationTwo(state, payload){
        console.log("called from another mutation", payload)
    }
}

Sie können jedoch einfache Funktionen schreiben und diese können wiederverwendbar sein

function mysecondfn(state,payload){
{
// do your stuff here
}


const mutations = {
    mutationOne(state, payload){
mysecondfn(state,payload)
     },

}

-1
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
}

Dies sollte das Problem beheben. Sie können das Commit aus der Aktion in Ihre Mutation einfügen, damit Sie aus Ihrer Mutation ein Commit durchführen können. Hoffe das hilft


Bitte fügen Sie Ihrem Code eine Erklärung hinzu: Was genau muss geändert werden und warum? Denken Sie daran, dass das OP in der Lage sein sollte, aus Ihrer Antwort zu lernen
Nico Haase
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.