Vuex: Proposal: Rename `mutations` to `setters`

Created on 16 Jul 2017  路  22Comments  路  Source: vuejs/vuex

What problem does this feature solve?

It simplifies the core proposition.

Calling it a "setter":

  • removes the jargon
  • demystifies the concept
  • makes the conceptual pairing of get / set clearer
  • more closely aligns with computed properties get() / set()
  • removes the issue of confusion of "mutations" vs "actions"
  • simplifies the language we use to describe the systems we're building
  • makes code, documentation and tutorials easier to grok

The fact that the state is so-called "mutated" is inconsequential; we have a state, we call methods on it, values are updated or returned.

What does the proposed API look like?

Store:

export default {
  state,
  actions,
  setters,
  getters,
}

Helpers:

import { mapState, mapGetters, mapSetters, mapActions } from 'vuex'

export default {

  computed: {
    ...mapState(['foo', 'bar', 'baz']),
    ...mapGetters(['foo', 'bar', 'baz']),
  },

  methods: {
    ...mapSetters(['foo', 'bar', 'baz']),
    ...mapActions(['loadFoo', 'loadBar', 'loadBaz']),
  }
}

As an aside, it looks way neater too :)

discussion proposal

Most helpful comment

+1 for setters.
Mutation just makes me think of Xmen, TMNT, Marvel superheroes, etc.

All 22 comments

+1 for setters.
Mutation just makes me think of Xmen, TMNT, Marvel superheroes, etc.

The choice of the word mutation goes a bit deeper than that because we commit mutations and dispatch actions.

Technically the "commit" is a form of dispatch; you're dispatching parameters (the setter / mutator function and its arguments) to the committer function (which actually performs the mutation).

I have no problem with commit / dispatch, but "mutations" just feels like another layer of CS jargon when "setters/getters" feels more natural.

I think most people see one of Vue's best qualities being that it eschews the Angular / React-like requirement for esoteric vernacular; it would awesome to see that in Vuex as well.

I'm torn.

I can see your point, but in this case, I can also imagine that the familiariy of "setters" could also lead to some false assumptions - mainly that it's fine and even expected to use "setters" directly to mutate state - that'S what setters are for in any other scenario that we use the term in.

And while it si of course fine to commit mutations / call a "setter" without going through an action, the recommended approach is to not do that, and to use actions instead.

I hope you get what I'm trying to say. By calling them something different, we make them something special, and people immediatly get a feel that they may have to treat those things a bit differently than a normal setter.

Taking this into account, what if we further expand on the original proposal and rename actions to "methods" as well? Aligning the name with that of Vue instance methods further clarifies that mutations can be called either from component methods (if not reused) or store methods (if reused across components).

Actions are not the same as methods. you can do anything in methods. Every function on an object property is a method, you can define aribtrary arguments for methods etc.

actions however, have a very specific format and purpose:

  • they are only called via store.dispatch(), not directly
  • they receive a special argument (context) and only accept one aditional argument (the payload)
  • their purpose is to commit a mutation, and to handle (often async) side-effect necessary to achieve that goal.
  • they always return a Promise

I think it's misleading to name them like regular methods.

For me, the hardest thing when starting with Vuex was remembering the mental mapping (no pun intended) Actions -> dispatch(), Mutations -> commit().

Maybe because English is not my first language and it looked weird at first glance since the "logical" mapping was not so clear to me.

One of them commits, and the other one dispatches actions. So, what about calling them Committers and Dispatchers? We'd still reduce the cognitive overload while make them "special enough" to prevent confusion with regular setters.

  computed: {
    ...mapState(['foo', 'bar', 'baz']),
    ...mapGetters(['foo', 'bar', 'baz']),
  },

  methods: {
    ...mapCommitters(['foo', 'bar', 'baz']),
    ...mapDispatchers(['loadFoo', 'loadBar', 'loadBaz']),
  }

Would it be feasible to still commit mutations, but have them as setters in the root state/module definition, or would it bloat the code too much?

I mean, I like the idea of committing mutations but also defining setters, I actually look for the setters section sometimes when I go back to a Vuex module file.

I agree that get, set seems more natural. When I was starting I also had difficulty linking commit to mutations and dispatch to actions.

I wouldn't mind the following terminology:

store definition:
getters, setters, actions

usage:
this.$store.get.book
this.$store.set.book(book)
this.$store.act.load()

getters get, setters set, and actions act

Mutations are similar to setters and actions are similar to methods. They aren't the same, though, because the affected the store (application state, not component data). The existing naming conventions force you to be aware that you're changing the store. Even if you blur it by using mapMutations and mapActions, you still see that you're mapping them in your component.

Solution 1
Use mapMutations and mapActions in your components.

The only thing mapMutations and mapActions don't do is give you a computed with get() and set(). I see unintended store changes as a big enough risk (and big enough pain to track down) that I'd rather make it obvious I'm changing the store (write my own computed with get() and set()).

Solution 2
For those who want to directly change state, mapState seems to be your answer. Testing this, it looks like setting a value bound with mapState changes the store. Doing so seems confuse VueDevtools (doesn't show changes to store data).

Note that I'm testing in a namespaced store module and calling mapState in computed in my component. My mutations are simple assignments, so your mileage may vary if you have complex mutations.

// in store: someValueGetter: state => state.someValue
// select the component that calls mapState in VueDevtools
console.log($vm0.$store.getters['namespace/someValueGetter')
// ==> ''
console.log($vm0.state.someValue)
// ==> ''
$vm0.state.someValue = 'hello'
console.log($vm0.state.someValue)
// ==> hello

// select a different component in VueDevtools
console.log($vm0.$store.getters['namespace/someValueGetter')
// ==> hello

More definitively, if I dispatch an action that saves the store data to the database, I find someValue contains 'hello' in the database. So changing mapped state definitely changes the store in the case I tested.

Hey @jmjf and everyone, thanks for all your input.

So I posted this proposal as a kind of finger in the dam for what is probably one of the more esoteric parts of Vue, Vuex, as an attempt to de-mystify some of the concepts and make it easier to grasp - call a spade a spade and all that.

What it seems to have done is galvanised proponents of the complexities of Vuex to say "No - we need it complicated! It reminds us we're doing big, powerful things!" (OK, I'm being a little cheeky here).

I see unintended store changes as a big enough risk (and big enough pain to track down) that I'd rather make it obvious I'm changing the store (write my own computed with get() and set()).

The "explicit getters and setters" argument seems to be a favourite of the single-tree state champions, but I find it to be a misnomer.

In 6 months we've not had one "unintended state change". The bugs we get are generally something NOT being wired, generally because of the extra layer of function access and boilerplate that Vuex requires, which makes it hard for folks to grok the app.

As for tracking down, that's not that hard either. Check Vue devtools's mutations, events and state, switch to the source tab type CMD P and type the name of a component in, set a breakpoint, catch the change, check the variables, navigate up the stack, and you'll usually track something down pretty quick.

I'm working now on probably the most complicated app I've ever had the chance to build, and I'm really glad I chose Vuex. Being able to look in one place, using the debug tools, and being able to track updates is invaluable.

However, after months and months of writing lots of boilerplate code, and essentially becoming a wiring technician I decided to bite the bullet and code up a whole bunch of ideas I was having into a formal wrapper around Vuex that manages ALL the wiring and gives me the simplest way to connect components to the store with a really simple API.

It does all sorts of things, including automatic computed setters and getters, including reaching into objects and setting getting sub properties, aliasing, fetching initial data, copies of data, resetting data, and most usefully providing a simple facade for getters/state and commits/actions in the form of get(), set() and sync().

Here's a screenshot of it in use (note the complex UI, but the simple wiring):

image

We now spend so little time in wiring the store (it's all one-liners) there's virtually no boilerplate, and we know it will just work. We get to spend so much more time on just building cool stuff.

So the bottom line is, I no longer care what Vuex wants to call things; it's really the smallest problem Vuex has. The biggest one IMHO is the "culture of complexity" around it.

Anyway, love Vuex, just think it's made something ish-complicated, overly complicated... but I respect that those decisions were taken for good reasons.

I can still write a wrapper, eh :)

@davestewart Best of both worlds--simplifies the code in the component and makes the fact you're fronting the store obvious by the API you developed. :)

@davestewart would you mind sharing your wrapper? I'd be very interested in having a look, we're attempting to do something similar.

@jannesiera absolutely, but not going to happen for a couple of weeks as we're at the end of the project and frantically trying to get the thing live! I should have some time in mid Dec to document and release though. Feel free to hit me up then :)

@davestewart will do!

Been watching this discussion since the beginning... got kinda tired of not seeing any reasonable solution to this. Tried to write a wrapper similar to one of @davestewart, but ended up making another vuex-like library.

The company I work in is currently in the process of replacing Vuex with this lib in one of our projects, btw.

https://github.com/Raiondesu/Tuex
I hope somebody will find it helpful.

Contributions are very welcome.

And while it si of course fine to commit mutations / call a "setter" without going through an action, the recommended approach is to not do that, and to use actions instead.

I always considered this a confusing part of Vuex. Imo, mutations should only be available through context in actions, so you can skip the "should I commit or mutate this?" question in components. I think this would make Vuex easier to reason about in general, because it creates a standard flow.

Hey all, + @jmjf, @jannesiera, @Raiondesu ...

So I've spent a couple of months wrapping up that code into a lean and mean OSS package.

pathify

It's called Pathify; here's the feature list:

  • declarative, state-based, path format
  • simplified store member access
  • transparent sub-property access
  • one-liner component data-binding
  • one-liner boilerplate generation
  • massive reduction in lines of code

It's officially in beta, check out the full docs here:

Need to finish the demos, check it all over and write tests, but for the main part, it's ready to go.

Please check it out!

The naming used on Vuex seems to complicate the understanding of it coming from VueJs.

State could be called data
Getters could be called computed
Mutations could be called setters
Actions could be called methods

_Voil脿_

After all, this is what they look like from the VueJs perspective.
I find myself going back and forth to the documentation because of the jargon.

I see no reason to change the vernacular. It works for the concepts used. It's not Googlish at all.

It also keeps the ideas decoupled from the existing component structure of Vue, which is important architecturally and conceptually.

@rynnova this ticket is also three years old...

@davestewart Happy to help!

Was this page helpful?
0 / 5 - 0 ratings