【问题标题】:Can I call commit from one of mutations in Vuex store我可以从 Vuex 商店中的突变之一调用提交吗
【发布时间】:2017-03-22 02:55:06
【问题描述】:

我有一个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 的答案,它似乎有你需要的东西,也许可以测试一下并用最新的答案更新这个论坛?谢谢!
  • 为什么不使用一个动作并在其中调用多个突变?

标签: javascript vue.js vuex


【解决方案1】:

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

this._mutations.mutationFunction[0]()

【讨论】:

    【解决方案2】:

    我认为

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

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

    【讨论】:

      【解决方案3】:

      我更喜欢拨打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。无论如何,时间旅行应该有效。如果我错了,请纠正我。

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

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

          编辑:我偶然发现了一个非常相似的问题,我的解决方案是使用 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)
           }
          }
          

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

          【讨论】:

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

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

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

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

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

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

          【讨论】:

            【解决方案9】:

            当你已经在做一个突变时,没有办法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 的角度来看,这在语义上更正确,因为 action 主要是为异步操作设计的。
            【解决方案10】:

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

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

            【讨论】:

            • 是的,这应该是答案。
            • 为什么不呢?..使​​用vue-native-websocke,您需要在突变(SOCKET_ONMESSAGE)中处理来自ws服务器的数据...而且它们也无法从突变中调用动作。
            【解决方案11】:

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

            【讨论】:

              【解决方案12】:

              要在突变之间共享代码,您必须创建一个执行该工作的新函数,然后您可以重复使用该函数。幸运的是,突变只是普通的旧函数,我们可以随意传递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)。
              【解决方案13】:

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

              【讨论】:

              • 寂静震耳欲聋。我也很想回答这个问题。
              • @Nacho 查看我刚刚创建的答案。这并不理想,但它是我所知道的最好的
              • 我同意丹尼尔的观点。函数是重用代码的方式。
              • 是的 - 突变只是一个将状态作为参数并对其进行修改的函数。您可以声明任意数量的辅助函数并重用它们。
              • 只有在您有答案时才发布答案。如果您想问什么,请创建一个新线程。
              猜你喜欢
              • 2020-10-13
              • 2018-12-21
              • 2019-03-02
              • 1970-01-01
              • 2020-12-31
              • 1970-01-01
              • 2020-09-03
              • 1970-01-01
              • 2019-01-07
              相关资源
              最近更新 更多