Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Vuex types constants with module namespace?

Tags:

I have this Vuex module:

//modules/things.js const state = {   firstThing: 'abc',   secondThing: 'def', };  const getters = {   getFirstThing: state => state.firstThing,   getSecondThing: state => state.secondThing, };  const mutations = {   setFirstThing: (state, payload) => state.firstThing = payload,   setSecondThing: (state, payload) => state.secondThing = payload };  const actions = {};  export default {   namespaced: true,   // <------   state,   mutations,   actions,   getters }; 

I use namespaced: true flag and can work with this module like this:

this.$store.state.things.firstThing             // <-- return abc here this.$store.commit('things/setFirstThing', 10) this.$store.getters['things/getFirstThing']     // <-- return abc here 

If I will use constants like in Vuex official example, and refactor my modules/things.js file like this:

export const Types = {   getters: {     GET_FIRST_THING: 'GET_FIRST_THING',     GET_SECOND_THING: 'GET_SECOND_THING',   },   mutations: {     SET_FIRST_THING: 'SET_FIRST_THING',     SET_SECOND_THING: 'SET_SECOND_THING',   } };  const getters = {   [Types.getters.GET_FIRST_THING]: state => state.firstThing,   [Types.getters.GET_SECOND_THING]: state => state.secondThing, };  const mutations = {   [Types.mutations.SET_FIRST_THING]: (state, payload) => state.firstThing = payload,   [Types.mutations.SET_SECOND_THING]: (state, payload) => state.secondThing = payload }; 

I will have to use namespace prefix:

this.$store.commit('things/' + Types.mutations.SET_FIRST_THING, 10); this.$store.getters['things/' +  + Types.getters.GET_FIRST_THING]   

If I will include module namespace prefix to Types constant, I will have to use string prefix things/ for mutations/actions/getters declaration:

const getters = {   ['things/' + Types.getters.GET_FIRST_THING]: state => state.firstThing,   ['things/' + Types.getters.GET_SECOND_THING]: state => state.secondThing, }; 

How to avoid that?

like image 546
Antoine Bernard Avatar asked Dec 05 '17 04:12

Antoine Bernard


2 Answers

You can disable namespacing by namespaced: false and just use constants with prefixes:

export const Types = {   getters: {     GET_FIRST_THING: 'THINGS_GET_FIRST_THING',    // your namespace without '/' slash     GET_SECOND_THING: 'THINGS_GET_SECOND_THING',   },   // ... } 

- it will work.

But if you still want to keep namespaced: true in module and use constants also, you can define two types of constants: public and private:

export const Types = {                                               // <-- public   getters: {     GET_FIRST_THING: 'things/GET_FIRST_THING',     GET_SECOND_THING: 'things/GET_SECOND_THING',   },   mutations: {     SET_FIRST_THING: 'things/SET_FIRST_THING',     SET_SECOND_THING: 'things/SET_SECOND_THING',   } };  const _types = removeNamespace('things/', Types);                    // <-- private 

Then use private _types only inside Vuex module:

const getters = {   [_types.getters.GET_FIRST_THING]: state => state.firstThing,          [_types.getters.GET_SECOND_THING]: state => state.secondThing, };  //... 

and public Types outside module:

// some-component.vue this.$store.commit(Types.mutations.SET_FIRST_THING, 10); this.$store.getters[Types.getters.GET_FIRST_THING] // ... 

Also implement simple removeNamespace function in your new namespace-helper.js file:

export default function removeNamespace(namespace, types){   return _.reduce(types, (typeObj, typeValue, typeName) => {     typeObj[typeName] = _.reduce(typeValue, (obj, v, k)=>{       obj[k] = v.replace(namespace, '');       return obj;     }, {});     return typeObj;   }, {}); } 
like image 66
hedin Avatar answered Nov 01 '22 16:11

hedin


The answer from @hedin worked brilliantly for me, thank you!

The only issues I had were:

  1. I'm using Typescript.

  2. It could be a bit overly verbose, hurting readability. However type safety is more important to me, and I'm willing to tolerate some verbosity get type checking.

Inspired by his design, I Typescripted it and reduced the verbosity.

(I am using Vue 3 (with composition API) + Vuex 4 (with namespaced modules).)

Firstly, I created namespace-helper.ts that looks like this:

import _ from "lodash";  type NamespaceHelper = {   [name: string]: string; };  // Enhanced from @hedin, see https://stackoverflow.com/a/47646215/1360592 export default (   namespace: string,   types: any,   section: "getters" | "actions" | "mutations", ): NamespaceHelper => {   return _.reduce(     types,     (typeObj: NamespaceHelper, typeValue, typeName) => {       if (typeName === section) {         return _.reduce(           typeValue,           (obj: NamespaceHelper, v, k) => {             obj[k] = v.replace(namespace, "");             return obj;           },           {},         );       }       return typeObj;     },     {},   ); }; 

Then in my store module, I have:

const namespace = "things";  // For external use export const Types = {   getters: {     GET_FIRST_THING: `${namespace}/GET_FIRST_THING`,     GET_SECOND_THING: `${namespace}/GET_SECOND_THING`,   },   actions: {     DO_FIRST_THING: `${namespace}/DO_FIRST_THING`,     DO_SECOND_THING: `${namespace}/DO_SECOND_THING`,   },   mutations: {     SET_FIRST_THING: `${namespace}/SET_FIRST_THING`,     SET_SECOND_THING: `${namespace}/SET_SECOND_THING`,   }, };  // For internal use in the same store const _getters = removeNamespace(`${namespace}/`, Types, "getters"); const _actions = removeNamespace(`${namespace}/`, Types, "actions"); const _mutations = removeNamespace(`${namespace}/`, Types, "mutations");  // getters const getters: GetterTree<MyStoreState, RootState> = {   [_getters. GET_FIRST_THING]: (state) => {     return state.blah;   },   ... };  // actions const actions: ActionTree<MyStoreState, RootState> = {   [_actions.DO_FIRST_THING]: ({ commit }) => {     // do stuff here     ...     commit(_mutations.SET_FIRST_THING);   }, };  // mutations const mutations = {   [_mutations.SET_FIRST_THING]: (state: MyStoreState) => {     state.blah = "foo";   }, };  export default {   namespaced: true,   state,   getters,   actions,   mutations, }; 

And here is how I use it from a component:

<script lang="ts"> // imports go here, not shown for brevity  import { Types } from "@/store/modules/things";  export default defineComponent({   name: "Thing",   setup(props) {     const store = useStore<RootState>();      // I prefer singular for consuming getters and actions externally.     const { getters: getter, actions: action } = Types;      const firstThing = computed<ThingType>(() =>       store.getters[getter.GET_FIRST_THING],     );      store.dispatch(action.DO_FIRST_THING);      return {       firstThing,     };   }, }); </script>  
like image 26
Robin Zimmermann Avatar answered Nov 01 '22 17:11

Robin Zimmermann