Last active
March 7, 2023 06:45
-
-
Save baryla/dab7ebb745623dc70ff2d21f474591a1 to your computer and use it in GitHub Desktop.
Vue 3 + Vuex 4 - useStoreModule composable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useStore, Store } from "vuex"; | |
interface InternalModule<S, A, M, G> { | |
state: S; | |
actions: A; | |
mutations: M; | |
getters: G; | |
} | |
/** | |
* This function allows us to access the internal vuex properties and | |
* maps them in a way which removes the module prefix. | |
*/ | |
function getFromStoreByType<T>( | |
moduleName: string, | |
type: unknown, | |
isNamespaced: boolean | |
) { | |
if (isNamespaced) { | |
return Object.keys(type) | |
.filter((t) => t.startsWith(`${moduleName}/`)) | |
.reduce((acc, curr) => { | |
const typeName = curr.split("/").pop(); | |
const typeValue = type[curr][0]; | |
return { [typeName]: typeValue, ...acc }; | |
}, {}) as T; | |
} | |
return Object.keys(type).reduce((acc, curr) => { | |
const typeValue = type[curr][0]; | |
return { [curr]: typeValue, ...acc }; | |
}, {}) as T; | |
} | |
/** | |
* We have to wrap the getters in a Proxy because we only want to | |
* "access" the getter if it actually being accessed. | |
* | |
* We could technically use the getFromStoreByType function but | |
* the getter would be invoked multiple types on store instantiation. | |
* | |
* This is just a little cheeky workaround. Proxy <3 | |
*/ | |
function wrapGettersInProxy<G>( | |
moduleName: string, | |
getters: G, | |
isNamespaced: boolean | |
) { | |
return new Proxy(getters as Object, { | |
get(_, prop: string) { | |
if (isNamespaced) { | |
return getters[`${moduleName}/${prop}`]; | |
} | |
return getters[prop]; | |
}, | |
}) as G; | |
} | |
function isModuleNamespaced<S>(moduleName: string, store: Store<S>): boolean { | |
// @ts-ignore internal Vuex object that isn't typed. | |
return Boolean(store._modulesNamespaceMap[`${moduleName}/`]); | |
} | |
export default function useStoreModule<S = any, A = any, M = any, G = any>( | |
moduleName: string, | |
storeName?: string | |
): InternalModule<S, A, M, G> { | |
// @ts-ignore useStore doesn't yet accept a key as arg | |
const store = storeName ? useStore(storeName) : useStore(); | |
const state = store.state[moduleName]; | |
const isNamespaced = isModuleNamespaced(moduleName, store); | |
const actions = getFromStoreByType<A>( | |
moduleName, | |
// @ts-ignore internal Vuex object that isn't typed. | |
store._actions, | |
isNamespaced | |
); | |
const mutations = getFromStoreByType<M>( | |
moduleName, | |
// @ts-ignore internal Vuex object that isn't typed. | |
store._mutations, | |
isNamespaced | |
); | |
const getters = wrapGettersInProxy<G>(moduleName, store.getters, isNamespaced); | |
return { | |
actions, | |
mutations, | |
state, | |
getters, | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment