ChatGPT解决这个技术问题 Extra ChatGPT

我可以从 Vuex 商店中的突变之一调用提交吗

我有一个 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
}

我想从突变:SET_PRODUCTS 中调用突变:SET_CATEGORIES,但这给了我错误:

projectFilter.js:22 Uncaught (in promise) ReferenceError: commit is not defined(…)

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

相关(已关闭)问题:github.com/vuejs/vuex/issues/907
嗨@Saurabh,我已经测试了 Kubiwama Adrien 的答案,它似乎有你需要的东西,也许可以测试它并用最新的答案更新这个论坛?谢谢!
为什么不使用一个动作并在其中调用多个突变?

D
Daniel S. Deboer

如果你绝对必须提交两个突变,为什么不从一个动作中做呢?操作不必执行异步操作。您可以像使用 state 一样解构 action 中的 commit 方法,如下所示:

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

为什么不呢?..使用vue-native-websocke,您需要处理来自突变(SOCKET_ONMESSAGE)中的ws服务器的数据...而且它们也无法从突变中调用动作。
K
Kubwimana Adrien

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

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

这是 Vuex 的新(ish)功能吗?令人惊讶的是,有人花了两年半的时间才指出明确正确的答案。
我不太确定这是否被认为是最佳实践。但是,此答案直接回答了问题。没有提供其他人通过 action 提出的替代方案。我已经对其进行了测试,它似乎运行良好。我认为这个答案应该放在首位。如果有人有兴趣阅读更多证明,此解决方案已在 Vuex Github - Please add an ability to call mutation from another mutation #907 中讨论过。
顺便说一句,如果您的 modules 是命名空间的,即使它在同一个文件下,您也必须通过 this.commit('modulesName/mutationName') 访问它,以防万一有人想知道。如果您需要更多信息,最好提醒您只需在突变中执行 console.log(this),它似乎与 Vue 拥有相同的实例,您也可以从那里访问 $route
@IrfandyJip 谢谢,您的评论为我节省了几个小时的调试时间!
M
Mani

如果您已经在进行突变,则无法commit进行另一个突变。突变是改变状态的同步调用。在一个突变中,您将无法提交另一个突变。

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

如您所见,突变处理程序仅接收 statepayload,仅此而已。因此,您将 commit 作为 undefined

在上述情况下,您可以将 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})
    },
    // ...
}

编辑:请参阅 Daniel S. Deboer 提供的以下答案。正确的方法是从一个动作中提交两个突变,如他的回答中所述。


我想知道为什么不允许从另一个突变提交突变?一些突变可以是原子性的,以至于它们应该被更大的突变函数使用。在当前版本中,由于这个限制,应该有很多重复的代码。
是的,可以从另一个没有副作用的突变中进行提交。但是当我们有相互调用的突变时调试不正确的状态更改将导致非常糟糕的开发人员体验。我认为这就是为什么不推荐或不允许这样做的原因。但值得庆幸的是,我们有这个简单的解决方案——我们总是可以定义一个进行多个状态更改的单一突变,如这个问题/答案中所见。
恕我直言,答案如下(从一个动作中完成两件事)。它更干净,更具可读性。
@JCKödel 我同意,正确的答案应该是丹尼尔提供的以下答案(从动作中提交两个突变)。我将在上面的答案中添加注释,以参考下面更好的答案。
@Mani 为什么这比 Dani 的答案更受欢迎?我认为从 Vuex 的角度来看,这在语义上更正确,因为操作主要是为异步操作设计的。
D
Daniel Buckmaster

要在突变之间共享代码,您必须创建一个执行该工作的新函数,然后您可以重用该函数。幸运的是,突变只是普通的旧函数,我们可以随意传递 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,就可以了。
这对我有用,但我将函数实现保留为突变,只是从函数中调用了 mutation.setCategories(state)。
不幸的是,在 Typescript Vue 方法中,这不起作用,bc。无法访问/查看普通功能
N
Nacho

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


寂静震耳欲聋。我也很想得到这个答案。
@Nacho 看到我刚刚创建的答案。这并不理想,但这是我所知道的最好的
我同意丹尼尔。函数是重用代码的方式。
是的 - 突变只是一个将状态作为参数并对其进行修改的函数。您可以声明任意数量的辅助函数并重用它们。
请仅在您有答案时发布答案。如果您想问什么,请创建一个新线程。
E
Emile Bergeron

阅读 Vuex documentation on Actions,很清楚它们的用途。

提交突变而不是改变状态

可以包含任意异步操作

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

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

我认为使用操作执行多个突变没有任何问题。


j
jiv-e

在您的情况下,您应该考虑只有一个突变,即 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 进行更改。


G
Guillaume Meral

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

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

如您所见,您可以直接调用突变本身。 (正如丹尼尔所说,它们毕竟只是普通函数)我相信这是对原始问题的更合适的答案:它是一种无需代码重复或额外功能即可组合突变的实际方法


调用突变函数与提交突变不同。
你能详细说明一下吗?这真的是一个问题,因为我们是从另一个突变中调用它的吗?
例如,注册插件的处理程序不会为第二个突变调用。这意味着 Vue 开发工具不会显示您可以“穿越”到的突变列表中的第二个突变。
但是用例是一个单一的突变,它做两件事。我理解您的观点,但在这种情况下,您不希望商店状态具有不同步的产品和类别列表。
嗯,是的,这就是我所说的“Daniel Buckmaster 解决方案的替代方案”的意思。这是我的第一个 StackOverflow 答案,也许我应该评论他的解决方案。
A
Amio.io

我更喜欢调用 mutations.SET_CATEGORIES(state) 而不是: - 从人工 action 调用 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)
 }
}

我的观点是你不需要在 VueToolbox 中看到 SET_CATEGORIES。无论如何,时间旅行应该有效。如果我错了,请纠正我。


A
Ali KOCA

首先,将 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 );
},...

ß
ßãlãjî

我认为

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

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

}

J
JeffNhan

另一个适合我的解决方案:

this._mutations.mutationFunction[0]()

A
Andrei
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 应该能够从您的答案中学习
e
el_yonousi

你可以访问所有的 vuex

this.app.store.commit("toast/show", {
                   mssg:this.app.i18n.t('order.ordersummary.notifymessage'),
                    type: "danger",
                });

在 vuex 中访问 $i18n

this.app.i18n.t('order.ordersummary.notifymessage')

S
Sarwar Hasan

用这个

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

虽然此代码段可能会解决问题,但它没有解释为什么或如何回答问题。请include an explanation for your code,因为这确实有助于提高您的帖子质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。