39

Fiddle : here

I am creating a webapp with Vue 2 with Vuex. I have a store, where I want to fetch state data from a getter, What I want is if getter finds out data is not yet populated, it calls dispatch and fetches the data.

Following is my Vuex store:

const state = {
  pets: []
};

const mutations = {
  SET_PETS (state, response) {
    state.pets = response;
  }
};

const actions = {
 FETCH_PETS: (state) => {
      setTimeout(function() { 
            state.commit('SET_PETS', ['t7m12qbvb/apple_9', '6pat9znxz/1448127928_kiwi'])
    }, 1000)
 }
}

const getters = {
    pets(state){
    if(!state.pets.length){
        state.dispatch("FETCH_PETS")
    }
    return state.pets
  }
}

const store = new Vuex.Store({
  state,
  mutations,
  actions,
  getters
});

But I am getting following error:

Uncaught TypeError: state.dispatch is not a function(…)

I know I can do this, from beforeMount of Vue component, but I have multiple components which uses same Vuex store, so I have to do it in one of the components, which one should that be and how will it impact other components.

Saurabh
  • 71,488
  • 40
  • 181
  • 244
  • I'm looking for an answer to this as well. I understand what GuyC means, but the data I'm hoping to return is a large download that I don't want to trigger when the app first loads. Multiple components need it, but the user could access any of them first (or none of them). – skagzilla Apr 25 '18 at 14:59

4 Answers4

24

Getters can not call dispatch as they are passed the state not context of the store

Actions can call state, dispatch, commit as they are passed the context.

Getters are used to manage a 'derived state'.

If you instead set up the pets state on the components that require it then you would just call FETCH_PETS from the root of your app and remove the need for the getter

GuyC
  • 6,494
  • 1
  • 31
  • 44
  • 1
    Thanks for replying, two things: 1. If there are multiple components using the pets, in which components should it be fetched, What can be central location to do this? 2. Similarly this can not be done from computed as well? – Saurabh Nov 18 '16 at 14:54
  • 1. vuex is your central store so all of them can access it via a computed property, i.e. `computed: { pets () { return this.$store.state.pets } }` passing the store into the root of your app allows for this. 2. You could use the mounted method of your apps root but I wouldn't use a computed property to make async updates to vuex. Instead they can just watch the store (as above) and be updated when the async operation completes and the commit has passed the state its new pet data. – GuyC Nov 18 '16 at 15:11
  • to clarify point 1. I would fire the async vuex dispatch() within your apps root or a component that logically will be called when the pets object is first required. Without seeing the apps structure that's hard to pinpoint but I'm sure you'll find a location that would make sense. – GuyC Nov 18 '16 at 15:14
  • 1
    I updated your fiddle to show how this can work: https://jsfiddle.net/thebigsurf/9a6Lg2vd/7/ – GuyC Nov 18 '16 at 16:44
13

I know this is an older post and I'm not sure if this is good practice, but I did the following to dispatch from a getter in my store module:

import store from "../index"

And used the store inside my getter like this:

store.dispatch("moduleName/actionName")

I did this to make sure data was made available if it was not already present.

*edit: I want you to be aware of this: Vue form - getters and side effects

This is related to @storsoc note.

If you need to dispatch from your getter you probably are already implementing your state wrong. Maybe a component higher up should already have fetched the data before (state lifting). Also please be aware that getters should only be used when you need to derive other data from the current state before serving it to your template otherwise you could call state directly: this.$store.state.variable to use in methods/computed properties.

Also thing about your lifecycle methods.. you could for example in your mounted or created methods check if state is set and otherwise dispatch from there. If your getter / "direct state" is inside a computed property it should be able to detect changes.

  • 1
    Definitely useful and saved us as well. Depending on use case, will need to watch out for triggering redundant or looping API calls, given that the getter is synchronous and the dispatched action asynchronous. Workaround is to also create a registry in the store of pending actions (we hashed it by action name plus serialized data argument) and ignore the action if pending, remove pending when complete. Not entirely content with this pattern yet, but it serves our case for just-in-time fetch-render that keeps user waiting a lot less. – storsoc May 11 '21 at 22:36
7

had the same Problem.. also wanted all Vue-Instances to automaticly load something, and wrote a mixin:

store.registerModule('session', {
    namespaced: true,
    state: {
        session: {hasPermission:{}},
        sessionLoaded:false
    },
    mutations: {
        changeSession: function (state, value)
        {
            state.session = value;
        },
        changeSessionLoaded: function (state)
        {
            state.sessionLoaded = true;
        }

    },
    actions: {
        loadSession(context)
        {
            // your Ajax-request, that will set context.state.session=something
        }
    }
}); 

Vue.mixin({
    computed: {
        $session: function () { return this.$store.state.session.session; },
    },
    mounted:function()
    {
        if(this.$parent==undefined && !this.$store.state.session.sessionLoaded)
        {
            this.$store.dispatch("session/loadSession");
            this.$store.commit("changeSessionLoaded");
        }
    },
});

because it loads only one per vue-instance and store and it it inlcuded automaticly in every vue-instance, there is no need to define it in every main-app

mech
  • 617
  • 6
  • 16
0

I use a getter to configure a dynamic page. Essentially, something like this:

getter: {
  configuration: function () {
    return {
      fields: [
        {
          component: 'PlainText',
          props: {},
          setPropsFromPageState: function (props, pageState, store) {
            // custom logic
          }
        }
      ]
    };
  }
}

Then in the page component, when I am dynamically setting the props on a dynamic component, I can call the setPropsFromPageState(field.props, this.details, this.$store) method for that component, allowing logic to be set at the config level to modify the value of the props being passed in, or to commit/dispatch if needed.

Basically this is just a callback function stored in the getter that is executed in the component context with access to the $store via it.

Jaredcheeda
  • 1,657
  • 17
  • 15