26

I would like to assign setter methods via mapState. I currently use a workaround where I name the variable that I am interested in (todo) as a temporary name (storetodo) and then refer to it in another computed variable todo.

methods: {
    ...mapMutations([
        'clearTodo',
        'updateTodo'
    ])
},
computed: {
    ...mapState({
        storetodo: state => state.todos.todo
    }),
    todo: {
        get () { return this.storetodo},
        set (value) { this.updateTodo(value) }
    }
}

I would like to skip the extra step and define the getter, setter directly within mapState.

Why would I want to do this?

The normal approach would be use mapMutations/mapActions & mapState/mapGetters without the computed get/set combination that I have illustrated above and to reference the mutation directly in the HTML:

<input v-model='todo' v-on:keyup.stop='updateTodo($event.target.value)' />

The getter/setter version allows me to simply write:

<input v-model='todo' />
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
Chris
  • 13,100
  • 23
  • 79
  • 162
  • Why don't you just define getters inside `computed` block with `mapGetters` to get values, and mutate state using mutations? The pattern is pretty clear, why would you try to break it? – Egor Stambakio May 30 '17 at 21:44
  • 1
    @wostex because my approach allows me to use v-model with two way databinding. I edited the question to illustrate it better. – Chris May 31 '17 at 06:49

3 Answers3

32

You can't use a getter/setter format in the mapState

what you can try is directly return the state in your get() and remove mapState from the computed property

computed: {
    todo: {
        get () { return this.$store.state.todos.todo},
        set (value) { this.updateTodo(value) }
    }
} 

Here is a related but not same JsFiddle example

Vamsi Krishna
  • 30,568
  • 8
  • 70
  • 78
6

This is my current workaround. Copied from my personal working project

// in some utils/vuex.js file 
export const mapSetter = (state, setters = {}) => (
  Object.keys(state).reduce((acc, stateName) => {
    acc[stateName] = {
      get: state[stateName],
   };
   // check if setter exists
   if (setters[stateName]) {
      acc[stateName].set = setters[stateName];
   }

   return acc;
 }, {})
);

In your component.vue file

  import { mapSetter  } from 'path/to/utils/vuex.js';

  export default {
    name: 'ComponentName',
    computed: {
      ...mapSetter(
        mapState({
          result: ({ ITEMS }) => ITEMS.result,
          total: ({ ITEMS }) => ITEMS.total,
          current: ({ ITEMS }) => ITEMS.page,
          limit: ({ ITEMS }) => ITEMS.limit,
        }),
        {
          limit(payload) {
            this.$store.dispatch({ type: TYPES.SET_LIMIT, payload });
          },
        },
      )
    },
  }

now you can use the v-model bindings. l

Kharel
  • 819
  • 8
  • 16
  • Good inspiration for my own solution to this. I made a version that traverses an object or a "subobject" of state and create properties (getters and setters) like so: ...makeProperties(this.$store.somestate, 'subobject'). The downside is, it bypasses mutations, but the use case for this for me is just a simple input form with lots of fields. I could make a version that uses mutations to commit changes instead of just assigning values, but then I'd need to write a million mutations and the points is brevity – U2ros Sep 21 '20 at 09:18
1

Another way of approaching that is using store mutations like below:

//in your component js file:
this.$store.commit('setStoretodo', storetodo)

Assuming you define setStoretodo in mutations of your vuex store instance (which is something recommended to have anyways):

//in your vuex store js file:
state:{...},
actions: {...}
...
mutations: {
    setStoretodo(state, val){
        state.storetodo = val
    },
    ...
}
...

That keeps the property reactive as mapState will grab the updated value and it will be rendered automatically.

Surely, that's not as cool as just writing this.storetodo = newValue, but maybe someone will find that helpful as well.

vir us
  • 9,920
  • 6
  • 57
  • 66