我可以从Vuex存储中的一种变异中调用提交吗


78

我有一个vuex商店,如下所示:

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
}

我想称呼mutation:SET_CATEGORIESfrom突变:SET_PRODUCTS,但这给了我错误:

projectFilter.js:22未捕获(承诺中)ReferenceError:未定义提交(…)

什么应该是正确的方法来做到这一点。我尝试了store.committhis.commit,但是这些也给出了类似的错误。


1
相关(已关闭)问题:github.com/vuejs/vuex/issues/907
阿米尔·阿里·

@Saurabh,您好,我已经测试了Kubiwama Adrien的答案,看来它具有您所需要的,也许可以测试一下并用最新答案更新此论坛?谢谢!
Irfandy Jip

为什么不使用动作并在其中调用多个突变呢?
8

Answers:


47

如果您已经在进行突变,则无法进行commit另一种突变。变异是一种同步呼叫,会改变状态。在一个突变中,您将无法进行另一个突变。

这是Vuex的API参考:https ://vuex.vuejs.org/en/api.html

如您所见,变异处理程序仅接收statepayload,仅接收。所以你得到commitundefined

在上述情况下,您可以将PRODUCT和CATEGORIES设置为同一更改处理程序的一部分,并且可以一次提交。您可以尝试以下代码是否有效:

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

编辑:请参阅下面的答案,由丹尼尔·S·德博尔提供。正确的方法是一次执行两个突变,如他的回答所述。


6
我想知道为什么不允许从另一个突变中进行一个突变?一些突变可能是原子的,以至于较大的突变函数应该使用它们。由于当前的限制,在当前版本中,应该有很多重复的代码。
阿斯坎

是的,有可能来自另一个突变而没有副作用。但是,当我们有互相调用的突变时,调试不正确的状态更改将导致非常糟糕的开发人员体验。我认为这就是为什么不建议或不允许这样做的原因。但值得庆幸的是,我们有这个简单的解决方案-我们总是可以定义一个可以进行多个状态更改的单个突变,如本问题/解答所示。
玛尼

恕我直言,答案在下面(一次行动就做两件事)。它更干净,更易读。
JCKödel

@JCKödel我同意,正确的答案应该是丹尼尔(Daniel)提供的以下答案(从动作中做出两个突变)。我将在上面的答案中加注,以参考下面的更好答案。
玛尼

@Mani为什么不比Dani的答案更好?从Vuex的角度来看,我认为这在语义上是更正确的,因为操作主要是为异步操作设计的。
Alex Napitupulu

110

如果您绝对必须犯两个突变,那为什么不从一个动作中做到呢?动作不必执行异步操作。您可以像处理状态一样,在操作中分解commit方法,如下所示:

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

4
是的,这应该是答案。
Thiago Yoithi

3
为什么不呢?..使​​用vue-native-websocke,您需要处理来自ws服务器的突变中的数据(SOCKET_ONMESSAGE)...而且它们也无法从突变中调用操作。
君士坦丁·德拉罗什

56

作为记录。要从突变方法中调用其他突变,请执行以下操作:

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

6
这是Vuex的新功能吗?令人惊讶的是,有人花了两年半的时间指出了明确正确的答案。
迈克尔·海斯

3
我不确定这是否被视为最佳做法。但是,此答案直接回答了问题。没有提供其他人通过提出的建议action。我已经对其进行了测试,它似乎运行良好。我认为这个答案应该放在首位。此解决方案已在Vuex Github中进行了讨论-如果有兴趣阅读更多证据,请增加从另一个突变#907调用突变的功能
Irfandy Jip

15
顺便说一句,如果您modules的名称空间已命名,即使它在同一文件下,您也必须通过访问它this.commit('modulesName/mutationName'),以防万一有人在想。如果您需要更多信息,则始终可以console.log(this)在突变内部进行操作,这似乎总是一个很好的提醒,它似乎与拥有相同的实例Vue,您也可以$route从那里访问它。
Irfandy Jip

30

要在突变之间共享代码,必须创建一个执行该工作的新函数,然后可以重用该函数。幸运的是,变异只是普通的旧函数,我们可以随意传递state参数,所以这很容易做到。

例如:

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

我认为,这给有关链接突变的状态之外的错误
德鲁·贝克

什么让你有那个想法?只要您仅从setCategories突变内调用,就可以了。
丹尼尔·巴克马斯特

这对我有用,但是我把函数实现留作一个突变,只是从函数中调用了mutations.setCategories(state)。
杰森

12

如果我有一些通用代码会影响多个突变之间的状态,我是否必须在所有突变上重复相同的代码?还是有更好的方法做到这一点?


8
沉默震耳欲聋。我也想回答这个问题。
daninthemix

1
@Nacho看到我刚刚创建的答案。这不是理想的,但我所知道的是最好的
Daniel Buckmaster

我同意丹尼尔。函数是重用代码的方式。
cstricklan

1
是的-变异只是将状态作为参数并对其进行修改的函数。您可以声明任意数量的帮助程序函数,然后重复使用它们。
克里斯威廉姆森

1
请仅在有答案时发布答案。如果您要提出问题,请创建一个新线程。
Pablo

5

阅读有关ActionsVuex文档,很清楚它们的用途。

  • 提交突变而不是改变状态
  • 可以包含任意异步操作

动作可以(不是必须)包含异步代码。其实下面的例子是正确的

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

使用动作执行多个突变时,我看不到任何问题。


3

在您的情况下,您应该考虑只有一个突变,即SET_PRODUCTS。

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

您永远不需要单独调用SET_CATEGORIES。想想看!只有更改产品,类别才会发生变化。产品只能通过SET_PRODUCTS进行更改。


1

编辑:我偶然发现了一个非常类似的问题,对我来说,解决方案是使用vuex getter:https ://vuex.vuejs.org/en/getters.html
您的类别实际上是产品的“计算”版本。将类别用作获取者可以使它们与产品保持同步,并避免重复存储中的数据。

为了回答标题中的问题,我保留了原始答案。
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)
 }
}

如您所见,您可以直接调用突变本身。(正如Daniel所说的,毕竟它们只是简单的函数)
我相信这是对原始问题的更恰当的答案:这是一种在没有代码重复或额外功能的情况下构成突变的实际方法。


调用一个突变的功能是不一样的作为的突变。
Emile Bergeron

您能详细说明一下吗?因为我们从另一个突变中称呼它,这真的是一个问题吗?
Guillaume Meral '18

例如,第二个突变不会调用已注册插件的处理程序。这意味着Vue开发人员工具不会在您可以“时间旅行”到的突变列表中显示第二个突变。
Emile Bergeron

但是用例是一个可以做两件事的单一突变。我理解您的意思,但是在那种情况下,您不希望拥有状态已同步的产品和类别列表的商店状态。
纪尧姆·梅拉(Juillaume Meral)'18年

1
嗯,这就是我所说的“ Daniel Buckmaster解决方案的替代方案”。这是我的第一个StackOverflow答案,也许我应该对此发表评论。
纪尧姆·梅拉(Juillaume Meral)'18年

1

首先,将Vue按钮分配给变量: 在main.js中:

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

然后将“ app”变量导入定义突变的js文件中: 在modules.js中:

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

您现在可以将其用作“ app。$ store.commit”:

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

0

我更喜欢调用mutations.SET_CATEGORIES(state)而不是:-从人工操作中调用2个不同的提交 -或commit()在突变内部进行操作,因为这会使单元测试更加困难。

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

我的意见是您不需要SET_CATEGORIES在VueToolbox中看到。无论如何,时间旅行应该工作。如果我错了,请纠正我。


0

我认为

从另一个变异中调用变异是一个坏主意,因为难以调试状态和组件

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

但是您可以编写简单的函数并且该函数可以重用

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
}

这应该解决它。您可以从操作中将提交注入到您的突变中,以便可以从您的突变中进行提交。希望这可以帮助


请在您的代码中添加一些解释:到底需要更改什么,为什么?请记住,OP应该能够从您的答案中学习
Nico Haase
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.