Accessing getters within Vuex mutations
Asked Answered
H

7

80

Within a Vuex store mutation, is it possible to access a getter? Consider the below example.

new Vuex.Store({
    state: {
        question: 'Is it possible to access getters within a Vuex mutation?'
    },
    mutations: {
        askQuestion(state) {
            // TODO: Get question from getter here
            let question = '';

            if (question) {
                // ...
            }
        }
    },
    getters: {
        getQuestion: (state) => {
            return state.question;
        }
    }
});

Of course the example doesn't make much sense, because I could just access the question property directly on the state object within the mutation, but I hope you see what I am trying to do. That is, conditionally manipulating state.

Within the mutation, this is undefined and the state parameter gives access to the state object, and not the rest of the store.

The documentation on mutations doesn't mention anything about doing this.

My guess would be that it's not possible, unless I missed something? I guess the alternative would be to either perform this logic outside of the store (resulting in code duplication) or implementing an action that does this, because actions have access to the entire store context. I'm pretty sure that it's a better approach, that is to keep the mutation focused on what it's actually supposed to do; mutate the state. That's probably what I'll end up doing, but I'm just curious if accessing a getter within a mutation is even possible?

Holstein answered 6/4, 2017 at 14:57 Comment(4)
Could you maybe elaborate on why you'd need getters in the mutation? You're right that your example could easily refer to state.question. And, yes, any logic that would take a different action based on the current state should be handled in an action.Encrata
You can access getters in actions, because actions get context as the first argument. like this: actions: { action1: (context, payload) => { console.log(context.getters.getSomething); }}. Not sure you can do it in mutations though, as mutations receive only local 'state'.Octroi
@Encrata Because my real code is more complex than this, and I need to check something. The concrete example is adding a product to a cart, and I want to check if it's already there. I wanted to keep that logic out of the mutation to keep it clean. I'm pretty sure the best approach is to use an action as well, but was just curious if it's even possible to access getters within mutations - for whatever reason.Holstein
This feature has been requested but rejected by the Vuex team, see this issue for the whole discussion, explanations, and a few workaroundsGamopetalous
E
81

Vuex store mutation methods do not provide direct access to getters.

This is probably bad practice*, but you could pass a reference to getters when committing a mutation like so:

actions: {
  fooAction({commit, getters}, data) {
    commit('FOO_MUTATION', {data, getters})
  }
},
mutations: {
  FOO_MUTATION(state, {data, getters}) {
    console.log(getters);
  }
}

* It is best practice to keep mutations a simple as possible, only directly affecting the Vuex state. This makes it easier to reason about state changes without having to think about any side effects. Now, if you follow best practices for vuex getters, they should not have any side effects in the first place. But, any logic that needs to reference getters can run in an action, which has read access to getters and state. Then the output of that logic can be passed to the mutation. So, you can pass the reference to the getters object to the mutation, but there's no need to and it muddies the waters.

Encrata answered 6/4, 2017 at 15:17 Comment(4)
Thanks! I'll go with an action as that seems more appropriate. I agree that passing along the getters is a workaround, but I had not thought of that nevertheless. Thanks for the clarification! :-)Holstein
What is bad practice? Using getters in mutations or the specific solution?Macklin
Thank you @thanksd, so in short, do the dirty work in the actions, rather then in the mutations?Houseroom
these rules are preposterousMyke
G
12

If you put your getters and mutations in separate modules you can import the getters in the mutations and then do this:

import getters from './getters'

askQuestion(state) {
  const question = getters.getQuestion(state)
  // etc
}
Giffard answered 13/9, 2018 at 12:33 Comment(2)
Although what if your getter looks like this? export const mygetter = (state, getters) => (id) => { return state.blah }Propinquity
import mygetter from './getters'; askQuestion(state) { const question = mygetter(state)Giffard
D
8

If anyone is looking for a simple solution, @bbugh wrote out a way to work around this here by just having both the mutations and getters use the same function:

function is_product_in_cart (state, product) {
  return state.cart.products.indexOf(product) > -1
}

export default {
  mutations: {
    add_product_to_cart: function (state, product) {
      if (is_product_in_cart(state, product)) {
        return
      }
      state.cart.products.push(product)
    }
  },

  getters: {
    is_product_in_cart: (state) => (product) => is_product_in_cart(state, product)
  }
}
Dalrymple answered 10/3, 2020 at 0:41 Comment(0)
G
1

You also can reference the Store object within a mutation, if you declare the store as an expression, like this:

const Store = new Vuex.Store({
  state: {...},
  getters: {...},
  mutations: {
    myMutation(state, payload) {
      //use the getter
      Store.getters.getter
    }
  }
});

Gastroenteritis answered 7/11, 2018 at 18:48 Comment(2)
What goes in the {...}? Looks to me like you are creating a new store, not referencing the existing one.Propinquity
that's just shorthand for "state goes here" - I think the poster meant that you do this when creating your original store and then you can reference it via whatever const you assign it to, in this case, "Store"Taryntaryne
P
1

Vuex 4

const state = () => ({
  some_state: 1
})

const getters = {
  some_getter: state => state.some_state + 1
}

const mutations = {
  GET_GETTER(state){
    return getters.some_getter(state)
  }
}

Somewhere in you component

store.commit('GET_GETTER') // output: 2
Partnership answered 12/2, 2021 at 16:23 Comment(1)
This doesn't work if you getter itself use another getter, e.g. : (state, getters) => getters.counter + 1.Strait
P
0

Another solution is to import the existing store. Assuming you're using modules and your module is called foo, your mutator file would look like this store/foo/mutations.js:

import index from '../index'
export const askQuestion = (state, obj) => {
    let store = index()
    let getQuestion = store.getters['foo/getQuestion']
}

I'm not saying this is best practise but it seems to work.

Propinquity answered 23/11, 2018 at 18:8 Comment(0)
A
0

At least in Vuex 3/4, I've reached for this pattern numerous times: simply use this.getters.

Inside a mutation, regardless if you are inside a Vuex module or not, this is always reference to your root store.

So:

mutations: {
  doSomething(state, payload) {
    if (this.getters.someGetter) // `this.getters` will access you root store getters
  }

Just to be extra clear, this will work if the getter you're trying to access belongs to your root store OR to any other non-namespaced module, since they are all added (flattened) to this.getters.

If you need access to a getter that belongs to any namespaced Vuex module, just remember they are all added to the root store getters as well, you just have to use the correct key to get to them:

this.getters['MyModuleName/nameOfGetter']

The great thing about this is that it also allows you to invoke mutations that belong either to the root store or any other store. this.commit will work exactly as expected as well Source

Ataliah answered 8/1, 2023 at 16:52 Comment(1)
I am using Vuex 4.1 and this is MutationTreeStreamy

© 2022 - 2024 — McMap. All rights reserved.