Vuex: The need for getters with payload

Created on 15 Dec 2016  路  16Comments  路  Source: vuejs/vuex

Let me state right from the beginning, that's a feature request.

I saw the discussion inside #52, especially quoting @yyx990803:

Why are you expecting payload inside getters? Computed getters do not take any arguments.


The thing is that I'm having a couple of use cases where being able to pass _payload_ to getters would be _very_ handy. I know, they're technically not functions thus you can't pass any arguments to them.

Right now, I'm forced to extract all of my logic in functions that accept current state as the first argument and payload as the second one (I'm naming them helpers, to be specific). I'm forced to do this often, practically every app I used to work on has the need of getters with some payload.

It would be nice if vuex will adapt this pattern into itself, I'm seeing its usage as follows:

new Vuex.Store({
  helpers: {
    // type here is similar to what we have for mutations
    GET_SOME_DATA (state, { id, type }) {
      return my_awesome_computation(state, id);
    }
  }
})

// somewhere in my components
vm.$store.extract({type: 'GET_SOME_DATA', id: 1});
// or
vm.$store.extract('GET_SOME_DATA', {id: 1});

The most important thing is that they'll be integrated with the modules system vuex has, and that's most convenient thing to have.

Let me know what do you think?

I can even try my hand around getting a PR for you guys, if you approve my idea.

2.0 proposal

Most helpful comment

@andreiglingeanu
Thank you for your proposal! I see your needs and I sometimes would like to pass some arguments to getters to query store state too.
However, I'm still not sure introducing the new API for that. Because we can achieve that by returning a function in getters.

getters: {
  filterProducts: state => type => { // only use state
    return state.products.filter(p => p.type === type)
  },

  filteredLength: (state, getters) => type => { // want to use getters
    return getters.filterProducts(type).length
  }
}

My concern is introducing a new concept would complicate to understand Vuex and its API. Since getters and helpers have the same responsibility (query store state), I'm not sure to have both getters and helpers in Vuex with more complexity.

@sotaan
This proposal seems to be similar to reactjs/reselect rather than reducer. Reducer is similar to mutations since reducer calculate next state from previous state and action (payload on Vuex).

All 16 comments

Talking about #460, it would be especially useful to also have helpers or whatever we name them available in watchers.

Could you explain a bit more about what exactly the advantages of this are? It appears to me that the need for this, in many situations, comes from not fully embracing the global state concept.

In my experience, this can often be avoided by also dispatching the id (from your example) in the store, and have a getter like currentPost which returns the post for that id.

For the few situations left where this may not be useful, because we only use this "getter with params" in one component locally - why move it to the store at all? You have to write teh functionality either way.

Don'T get me wrong, I'm open to learn about common usecases where this solves a real need, but so far, I haven't personally come across these siutations appearantly.

Looking forward to your feedback :)

It seems that what you asking for is a reducer feature.
Checkout official Redux docs and some tutorials online.

@LinusBorg Thank you for your response!

I'll try to describe my use case:

I have a complicated widget that has a state and a nested components structure that displays and triggers mutations in the state of that widget. The crucial part is that I may have multiple widgets per page and the requirement is that they should be completely separated from each other. Also, they happen to live in separated Vue instances - because they are $mount-ed and $destroy-ed by demand.

My first attempt at it was to create separated Vuex.Store instances for each of them, but vuex doesn't play nice with multiple instances (also devtools are not staying cool with multiple stores).

I'm seeing two options from now on:

  • To have a global store that will keep each widget state in an array and I will be able to query each of them by a widget_id
  • To define a dynamic module inside the global store specific to each widget and to unregister the module at the $destroy time in order keep things clean

No mather which path I'll choose, the helpers feature will make my life a lot easier and I'm really seeing this as a feature other users may also want.

That is, I'm not really able to save currentWidgetId somewhere globally in the store in order for getters to able to pick up it. That's because widgets have they're own lifecycle and I totally don't want them to interfere.


@sotaan

It seems that what you asking for is a reducer feature.

I'm not that familiar with Redux concepts and I don't know if that's what I'm talking about. Can you elaborate your point a bit?

@andreiglingeanu checkout here you have great examples.
From the official Redux doc:

Actions describe the fact that something happened, but don't specify how the application's state changes in response. This is the job of a reducer.

@andreiglingeanu
Thank you for your proposal! I see your needs and I sometimes would like to pass some arguments to getters to query store state too.
However, I'm still not sure introducing the new API for that. Because we can achieve that by returning a function in getters.

getters: {
  filterProducts: state => type => { // only use state
    return state.products.filter(p => p.type === type)
  },

  filteredLength: (state, getters) => type => { // want to use getters
    return getters.filterProducts(type).length
  }
}

My concern is introducing a new concept would complicate to understand Vuex and its API. Since getters and helpers have the same responsibility (query store state), I'm not sure to have both getters and helpers in Vuex with more complexity.

@sotaan
This proposal seems to be similar to reactjs/reselect rather than reducer. Reducer is similar to mutations since reducer calculate next state from previous state and action (payload on Vuex).

However, I'm still not sure introducing the new API for that. Because we can achieve that by returning a function in getters.

That made the trick! Thank you, I'm totally closing my issue now. A live demo covering your idea.

@sotaan It really does look like reducers are a different concept which is not relevent to my proposal. Thank you for your response, though.

Ohh, love the returning function idea :) We should add it to the docs.

@yyx990803 I just tried it yesterday and it really is awesome :) Totally should go into the docs!

What do you think about call action inside getter? @ktsn

const getters = {
    //ES6
    getProductById: (state, getters) => (id) =>{
           const product = getters.products.find(x=> x.id === id);
           if (!product) dispatch('LOAD_PRODUCT', id);
           return product;
    },
};

@Ralf8686 You shouldn't do that. Getters should just return derived state.

@ktsn , sad( I am just do dictionary. Better, when need some item in dictionary, i should dispatch action in life circle hooks and in view template call getter. Right? I just wanted call only getter in components.

Getters will not only be called explicitly but also called implicitly when the returned value is used in template. In addition, they don't always be called as the returned value can be cached.
So if they have some side effects, it can easily cause unexpected behavior. That is the reason you should not call actions in getters.

@ktsn , one thing to note: normal Vuex getter functions are cached, but functions returned from getters are not. You probably don't want to put heavy computation in the functions you return from getters.

See #777

@ktsn thanks, that's informative

Was this page helpful?
0 / 5 - 0 ratings

Related issues

visualjerk picture visualjerk  路  3Comments

gorbypark picture gorbypark  路  3Comments

jbruni picture jbruni  路  3Comments

jdittrich picture jdittrich  路  3Comments

Nickvda picture Nickvda  路  3Comments