Search code examples
vue.jsvuex

Vuex mapState based on route and route parameters


I have a works component I use different pages on my app and I am trying to load the state based on the route and route parameters.

In my App.vue file I dispatch the async action to get the json file like

mounted() {
    this.$store.dispatch('getData')
},

And I map the state in my works component like that

export default {
    name: 'Works',
    computed: mapState({
        works: (state) => state.works.home.items.slice(0, state.works.home.loadedCount),
        loadedCount: (state) => state.works.home.loadedCount,
        totalCount: (state) => state.works.home.items.length,
    })
}

I actually need to map the state dynamically based on the route just like state.works[this.$router.currentRoute.params.category] or based on route name. Could you please tell me what is the correct way to get the data (async) from my state?

Vuex store:

export default new Vuex.Store({
    state: {
        works: {
            all: {
                items: [],
                loadedCount: 0,
            },
            home: {
                items: [],
                loadedCount: 0,
            },
            web: {
                items: [],
                loadedCount: 0,
            },
            print: {
                items: [],
                loadedCount: 0,
            },
        },
        limit: 2,
    },
    mutations: {
        SET_WORKS(state, works) {
            state.works.all.items = works
            works.map((el) => {
                if (typeof state.works[el.category] !== 'undefined') {
                    state.works[el.category].items.push(el)
                }
            })
        },
    },
    actions: {
        getData({ commit }) {
            axios
                .get('/works.json')
                .then((response) => {
                    commit('SET_WORKS', response.data.works)
                })
        },
    },
})

Solution

  • You can do it in beforeCreate hook.

    beforeCreate(){
        const category = this.$route.params.category;
    
        Object.assign(this.$options.computed, {
          ...mapState({
            categoryItems: (state) => state.categories[category],
          }),
        });
    }
    

    I've created a basic working example: https://codepen.io/bgtor/pen/OJbOxKo?editors=1111

    UPDATE:

    To get mapped properties updated with route change, you will have to force re-render the component. The best way to do it, is to change the component key when route change in parent component.

    Parent.vue

    <template>
      <categoryComponent :key="key"></categoryComponent> // <-- This is the component you work with
    </template>
    
    computed: {
      key(){
         return this.$route.params.category
      }
    }
    

    With this approach the beforeCreate hook will be triggered with every route change, getting fresh data from Vuex.