0

In the app I'm creating I have a base store for objects that might be used across the entire app, such as the logged in user, validation errors and the like.

I also have other namespaced modules for specific sections of my app.

When my parent component is loaded there is an ajax call that pulls in data and commits it to the various stores.

export const instantiate = ({ commit, dispatch }) => {
    return axios.get('/setup/fetch')
        .then((response) => {
            dispatch('base/setLoggedInUser', response.data.user, { root: true })
            commit('setFetishesList', response.data.fetishes)
            commit('setColorsList', response.data.colors)
            commit('setRolesList', response.data.roles)
            commit('setGendersList', response.data.genders)
            commit('setOrientationsList', response.data.orientations)
            commit('setLookingsList', response.data.lookings)
            commit('setSeekingsList', response.data.seekings)
            commit('setBodiesList', response.data.bodies)
            commit('setHeightsList', response.data.heights)
            commit('setEthnicitiesList', response.data.ethnicities)
            commit('setHairsList', response.data.hairs)
            commit('setEyesList', response.data.eyes)
            commit('setPiercingsList', response.data.piercings)
            commit('setTattoosList', response.data.tattoos)
            commit('setSmokingsList', response.data.smokings)
            commit('setDrinkingsList', response.data.drinkings)
            commit('setStatusesList', response.data.statuses)
            commit('setEducationsList', response.data.educations)
            commit('setAgesList', response.data.ages)

        return Promise.resolve(response)
    })
}

Then I use mapped getters to access items from my stores.

computed: {
    ...mapGetters({
        user: 'base/getUser',
        fetishList: 'setup/getFetishesList',
        localeData: 'setup/getLocale',
        colorsList: 'setup/getColorsList',
        rolesList: 'setup/getRolesList',
        genderList: 'setup/getGendersList',
        orientationList: 'setup/getOrientationsList',
        lookingList: 'setup/getLookingsList',
        seekingList: 'setup/getSeekingsList',
        validation: 'base/getValidationErrors',
    }),
}

All is working as expected except for my user object.

In my Vue inspector I can see that the user object is stored properly in Vuex as expected, but when I console.log(this.user) I get null and anytime I try to access a user property I get console errors.

Can anyone explain why this might be happening, I've never seen this before and have no idea what I'm looking for?

Thanks.

Vince Kronlein
  • 2,994
  • 4
  • 36
  • 60

2 Answers2

0

My guess is that your dispatch() (Vue.js actions are ALWAYS expected to be async) is not completing properly. This is how I would rewrite it with a single caveat:

Your base/setLoggedInUser Vuex action MUST return a Promise for this to work properly.

/*
export const instantiate = ({ commit, dispatch }) => {
    return axios.get('/setup/fetch')
        .then((response) => {
            dispatch('base/setLoggedInUser', response.data.user, { root: true })
            commit('setFetishesList', response.data.fetishes)
            commit('setColorsList', response.data.colors)
            commit('setRolesList', response.data.roles)
            commit('setGendersList', response.data.genders)
            commit('setOrientationsList', response.data.orientations)
            commit('setLookingsList', response.data.lookings)
            commit('setSeekingsList', response.data.seekings)
            commit('setBodiesList', response.data.bodies)
            commit('setHeightsList', response.data.heights)
            commit('setEthnicitiesList', response.data.ethnicities)
            commit('setHairsList', response.data.hairs)
            commit('setEyesList', response.data.eyes)
            commit('setPiercingsList', response.data.piercings)
            commit('setTattoosList', response.data.tattoos)
            commit('setSmokingsList', response.data.smokings)
            commit('setDrinkingsList', response.data.drinkings)
            commit('setStatusesList', response.data.statuses)
            commit('setEducationsList', response.data.educations)
            commit('setAgesList', response.data.ages)

        return Promise.resolve(response)
    })
}
*/

export const instantiate = ({ commit, dispatch }) => {
    return axios.get('/setup/fetch')
        .then((response) => Promise.all([
          dispatch('base/setLoggedInUser', response.data.user, { root: true }),
          Promise.resolve(response)
        ]))
        .then(([dispatchResponse, response]) => {
            commit('setFetishesList', response.data.fetishes)
            commit('setColorsList', response.data.colors)
            commit('setRolesList', response.data.roles)
            commit('setGendersList', response.data.genders)
            commit('setOrientationsList', response.data.orientations)
            commit('setLookingsList', response.data.lookings)
            commit('setSeekingsList', response.data.seekings)
            commit('setBodiesList', response.data.bodies)
            commit('setHeightsList', response.data.heights)
            commit('setEthnicitiesList', response.data.ethnicities)
            commit('setHairsList', response.data.hairs)
            commit('setEyesList', response.data.eyes)
            commit('setPiercingsList', response.data.piercings)
            commit('setTattoosList', response.data.tattoos)
            commit('setSmokingsList', response.data.smokings)
            commit('setDrinkingsList', response.data.drinkings)
            commit('setStatusesList', response.data.statuses)
            commit('setEducationsList', response.data.educations)
            commit('setAgesList', response.data.ages)

        return Promise.resolve(response)
    })
}
th3n3wguy
  • 3,166
  • 2
  • 20
  • 28
  • Great thanks I'll give that a try. Is it possible to commit directly to another namespace? I've never done anything but dispatch to an alternate module. – Vince Kronlein Mar 05 '20 at 21:11
  • @VinceKronlein => I don't think you're able to commit to another namespace. I'm pretty sure you have to use an action (like you are doing) in order to accomplish that, but again, I am not 100% confident with that answer. – th3n3wguy Mar 05 '20 at 21:32
  • Afaik committing directly is an antipattern. Commits should be fired in actions only. – Luckyfella Mar 05 '20 at 21:33
  • The commit is being fired from actions, just from actions in another namespace. And just for FYI, it works perfectly, I switched my state user from null to an empty array and all was resolved. I then switched the dispatch to a commit and I have perfection. – Vince Kronlein Mar 05 '20 at 21:37
  • @VinceKronlein you can commit to other namespaces using the {root: true} option, like so: commit('module/mutation', payload, { root: true }) Found at https://stackoverflow.com/questions/44618440/vuex-how-to-commit-a-global-mutation-in-a-module-action – cppstudy Mar 05 '20 at 21:39
  • @VinceKronlein => If this answer worked for you, can you please accept it for others in the future who might land on this page searching for a similar question/answer? – th3n3wguy Mar 05 '20 at 21:39
  • @th3n3wguy => while I appreciate your answer, it wasn't the solution. The solution was to change the default value of the user in state from null to empty array [] – Vince Kronlein Mar 05 '20 at 22:00
0

There are two main posibilities here:

The first one is that you might not be defining properly the user getter.

The second one, console.log is being executed previous to the data being set by this action:

 dispatch('base/setLoggedInUser', response.data.user, { root: true })

Vuex actions are asynchronous, so setLoggedInUser could have started before the console.log (and the code giving you errors) is executed, but the actual data might not have been received yet at that point (it would be undefined).

If this is the case, add the following condition to the part of the template or the component(s) that are using the block of code where you are getting those errors:

 v-if="user"

This will make Vue to wait for the mapped getter user to have a value to mount said template segment or components, avoiding trying to access properties of undefined.

cppstudy
  • 173
  • 1
  • 17
  • Thanks for that advice, very good. As it turns out, in my state, I had user: null, and once I changed it to user: [] all resolved itself. – Vince Kronlein Mar 05 '20 at 21:34
  • @VinceKronlein well, that's even better, as long as that default value is valid for all your cases. For example, if you try to do something like this.user[1] without checking if the user data has been set, then you will have a similar problem. – cppstudy Mar 05 '20 at 21:37