34

I have a list of filters I want to apply to a json object.

My mutations look like this:

const mutations = {
    setStars(state, payload) {
        state.stars = payload;
        this.dispatch('filter');
    },

    setReviews(state, payload) {
        state.reviews = payload;
        this.dispatch('filter');
    }
};

Because of how filters work I need to re-apply them all again since I can't simply keep downfiltering a list because this gets me into trouble when a user de-selects a filter option.

So when a mutation is being made to a stars filter or reviews filter(user is filtering) I need to call a function that runs all my filters.

What is my easiest option here? Can I add some kind of helper function or possible set up an action which calls mutations that actually filter my results?

Stephan-v
  • 19,255
  • 31
  • 115
  • 201
  • 1
    When you need to return a filtered data, there is no need to mutate your storage. Keep your data intact and create a getter which returns filtered data, either 2 getters (1 for each case) or a single getter which accepts an argument (filter condition). Related reading: https://vuex.vuejs.org/en/getters.html – Egor Stambakio Jun 01 '17 at 14:25
  • @wostex I would agree with you if it were just one filter but I need to apply multiple filters at once for a single value. So I can not just use 1 or 2 getters separately. They need to work together. – Stephan-v Jun 01 '17 at 14:26
  • A getter can call another getter. – Egor Stambakio Jun 01 '17 at 14:28
  • @wostex true but I would need any getter to call every other getter that is a filter and do this for all getters. It would get messy real fast. Perhaps I don't really see what you mean but thanks for the tip anyways! – Stephan-v Jun 01 '17 at 14:29
  • Here's what I mean basically: https://jsfiddle.net/wostex/u2yoLgLb/ – Egor Stambakio Jun 01 '17 at 14:49
  • @wostex Sorting seems like a different concept. With a filter I need to at least make a mutation to my store because my store needs to know what elements need be to filtered. Thanks for your example though, always helps. – Stephan-v Jun 01 '17 at 14:57
  • Filter is the same thing: https://jsfiddle.net/wostex/u2yoLgLb/1/ check a search field. What I'm trying to say is you don't really need to mutate something in most cases when you need a filter (just by Vuex design, it's not required however). This is how you can preserve data for future usage, when user clears filters for example. It's better in my opinion to filter inside a component itself or with a getter. Chained filters can be easier to implement in a component itself with methods. Maybe your app is totally different though, I don't know. – Egor Stambakio Jun 01 '17 at 15:11

2 Answers2

48

Mutations can't dispatch further actions, but actions can dispatch other actions. So one option is to have an action commit the mutation then trigger the filter action.

Another option, if possible, would be to have all filters be getters that just naturally react to data changes like a computed property would.

Example of actions calling other actions:

// store.js
export default {
  mutations: {
    setReviews(state, payload) {
      state.reviews = payload
    }
  }

  actions: {
    filter() {
      // ...
    }

    setReviews({ dispatch, commit }, payload) {
      commit('setReviews', payload)
      dispatch('filter');
    }
  }
}


// Component.vue
import { mapActions } from 'vuex';

export default {
  methods: {
    ...mapActions(['setReviews']),
    foo() {
      this.setReviews(...)
    }
  }
}
Matt
  • 43,482
  • 6
  • 101
  • 102
  • So with every mutation I do, I need to call a dispatch straight after it from my Vue components? Since I am not able to call an action from a mutation inside my Vuex store? – Stephan-v Jun 01 '17 at 14:19
  • 2
    @Stephan-v i would actually have the components dispatch a `setReviews` action _instead_ of a mutation. The _action_ can both the commit the `setReviews` mutation _and_ trigger the `filter` action. – Matt Jun 01 '17 at 14:21
  • Makes sense. How do I properly call another action from an action inside my Vuex store though? – Stephan-v Jun 01 '17 at 14:25
  • @Stephan-v actions receive `context` as their first parameter, which contains a `dispatch` function you can use. I updated my post with a quick, untested example – Matt Jun 01 '17 at 14:26
6

You can actually dispatch actions from a mutation by using this. For example:

const actions = {
  myAction({ dispatch, commit }, { param }) {
  // Do stuff in my action
  }
}

const mutations = {
  myMutation(state, value) {

    state.myvalue = value;
    this.dispatch('myModule/myAction', { param: 'doSomething' });
  },
}
gmm
  • 943
  • 1
  • 17
  • 30
  • 11
    I strongly discourage anyone from doing this. It is very much an anti-pattern. Mutations are very clearly intended to be synchronous / blocking code, where as actions are not. This is in essence undermining the strict role mutations have by permitting them to do more than they should do (that is, take some property of state and modify it with the input its received in some way). – SikoSoft May 16 '20 at 19:58
  • 1
    @Lev I agree that this is indeed an anti-pattern but the part about Actins being async only isn't true. Yes async code should be placed in Actions but Actions can as well contain synchronous code. – Hexodus Sep 24 '20 at 07:22