Vuex: [Feauture] Getters accessible in mutations

Created on 10 Mar 2017  路  9Comments  路  Source: vuejs/vuex

It would be nice (from performance point of view) to allow accessing getters when performing mutations, e.g.:

mutations: {
    MUTATION_NAME(state, payload, { getters }) {
        getters.someExpensiveGetter.forEach((item) => item.mutated = 'test');
    }
}

Is it possible and good idea? What do you think?

proposal

Most helpful comment

@LinusBorg getters are available for actions, but....
for synchronous changes to the database, actions are not needed, correct?
so in those cases ie: mutations without actions, it would be useful to have the getters (for local and root state)

Note: ommiting actions and going straight to mutations:

a) significantly reduces the boilerplate, and

b) removes the need to pass the mutation name by string
(a major smell, reminds one of the issues with Angular v1 !)

All 9 comments

We already had that request recently, If I recall correctly.

My opinion then was, and still is, that actions already have access to getters and it should be their job to prepare the data necessary for a commit.

But I haven't seen performance as an argument in this discussion yet. In what scenarios do you see performance optimizations with getters in mutations?

@LinusBorg I can understand your opinion, however I can't see how would it would work for all scenarios. Lets assume we have large collection state.collection = [...]. We have getter

filteredCollection({ state }) { 
    state.collection.filter((item) => item.status === 'open');
 }

How could I mutate all items in filteredCollection?

actions: {
    doSthWithFilteredCollection({ getters, commit }) {
        getters.filteredCollection.forEach((item, index) => {
            item.status = 'closed';
            commit('DO_SOMETHING', { item, index });
        })
    }
}

As you can see when passing index to commit function I will get index of already filtered filteredCollection so I can't easily update collection's item in DO_SOMETHING mutation.

Am I missing something?

My point is that I can't update items in large collection using caching power of getters. Every time when using mutation I have to do getter work(state.collection.filter((item) => item.status === 'open')) in case of getting only part of the collection, that I intend to change.

An easy, immediate solution would be to pass the getter result into the mutation, and mutate that. It will be the same objects anyway.

But it doesn't feel totally right, I get that.

The broader topic here is that arrays of objects are generally problematic, performance-wise, in global stare systems like vuex or redux.

The general consensus seems to be that its best to keep the objects in a map object, keyed by id, and then an additional array of ids can be used if the order of objects is important.

An ordered array of objects is then easily achievable through a getter, if necessary.

Thank you for the response. Regards.

Sorry for hijacking this issue but I didn't want to open a new one...

I have this mutation:

mutations: {
  // ...
  add_product_to_cart (state, product) {
    if (state.cart.products.indexOf(product) > -1) {
      return
    }
    state.cart.products.push(product)
  },
  // ...
}

But in other places I alreay need the following getter ...

getters: {
  // ....
  is_product_in_cart (state, getters) {
    return (product) => {
      return (state.cart.products.indexOf(product) > -1)
    }
  },
  // ...

... which I would also like to re-use in the mutation. Possible?

  1. This is a closed issue
  2. Issues are reserved for bugs and feature request. Support @ forum.vuejs.org or the gitter chat.

If anyone else finds this looking for an answer, you can just use a function outside of the mutations definition:

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

@LinusBorg getters are available for actions, but....
for synchronous changes to the database, actions are not needed, correct?
so in those cases ie: mutations without actions, it would be useful to have the getters (for local and root state)

Note: ommiting actions and going straight to mutations:

a) significantly reduces the boilerplate, and

b) removes the need to pass the mutation name by string
(a major smell, reminds one of the issues with Angular v1 !)

:+1: here.

As a consequence, instead of having one mutation per mutation type, to avoid too much repetition, I'm writing:

export const state = () => ([])

export const mutations {
  mutateObject(state, object, fields) {
    Object.assign(state.find(o => o === object), fields);
  }
}

export const actions {
  shouldHaveBeenAMutation({commit, getters}, field) {
    commit('mutateObject', getters.currentObject, {field})
  }
}

And suddenly all the cleanness is gone ...

Besides, I don't understand why vuex doesn't consider objects as immutable like redux does, this would be much less confusing. But that's another topic...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

weepy picture weepy  路  3Comments

visualjerk picture visualjerk  路  3Comments

gdelazzari picture gdelazzari  路  3Comments

jbruni picture jbruni  路  3Comments

ijse picture ijse  路  3Comments