Skip to content

Instantly share code, notes, and snippets.

@hendrysadrak
Last active January 7, 2024 01:10
Show Gist options
  • Save hendrysadrak/af379c8eeaa9ba377b8be809e1381a80 to your computer and use it in GitHub Desktop.
Save hendrysadrak/af379c8eeaa9ba377b8be809e1381a80 to your computer and use it in GitHub Desktop.
Vue 2.7, Composition API, Vuex
import { store, type RootState } from '@/store'
import { mapValues } from 'lodash'
import { computed, getCurrentInstance, type ComputedRef } from 'vue'
import type { ActionTree, GetterTree, MutationTree, Store } from 'vuex'
type GetRestParams<T extends (a: any, ...args: any) => any> = T extends (
a: any,
...args: infer P
) => any
? P
: never
export const useRootStore = (): Store<RootState> => {
const $root = getCurrentInstance()!.proxy.$root
return $root.$store as Store<RootState>
}
type GenericModule = {
state: Record<string, unknown>
getters: GetterTree<any, any>
actions: ActionTree<any, any>
name: string
}
type GenericStore = Store<any>
const _getActions = <
Module extends GenericModule,
Actions = Module['actions'],
ActionsMapped = {
[Name in keyof Actions]: (
...args: GetRestParams<Actions[Name]>
) => Promise<Awaited<ReturnType<Actions[Name]>>>
},
>(
{ actions }: GenericModule,
store: GenericStore,
namespace: string,
): ActionsMapped =>
mapValues(
actions,
(_, name) =>
async (...args: any[]) =>
await store.dispatch(`${namespace}/${name}`, ...args),
) as ActionsMapped
const _getState = <
Module extends GenericModule,
State = Module['state'],
StateMapped = { [Name in keyof State]: ComputedRef<State[Name]> },
>(
{ state }: GenericModule,
store: GenericStore,
namespace: string,
): StateMapped =>
mapValues(state, (_, name) =>
computed(() => store.state[namespace][name]),
) as StateMapped
const _getGetters = <
Module extends GenericModule,
Getters = Module['getters'],
GettersMapped = {
[Name in keyof Getters]: ComputedRef<ReturnType<Getters[Name]>>
},
>(
{ getters }: GenericModule,
store: GenericStore,
namespace: string,
): GettersMapped =>
mapValues(getters, (_, name) =>
computed(() => store.getters[`${namespace}/${name}`]),
) as GettersMapped
export const useStore = <
Module extends GenericModule,
State = Module['state'],
StateMapped = { [Name in keyof State]: ComputedRef<State[Name]> },
Getters = Module['getters'],
GettersMapped = {
[Name in keyof Getters]: ComputedRef<ReturnType<Getters[Name]>>
},
Actions = Module['actions'],
ActionsMapped = {
[Name in keyof Actions]: (
...args: GetRestParams<Actions[Name]>
) => Promise<Awaited<ReturnType<Actions[Name]>>>
},
>(
module: Module,
): {
actions: ActionsMapped
state: StateMapped
getters: GettersMapped
} => {
const store = useRootStore()
const namespace = module.name
return {
actions: _getActions(module, store, namespace),
state: _getState(module, store, namespace),
getters: _getGetters(module, store, namespace),
}
}
export const useActions = <
Module extends GenericModule,
Actions = Module['actions'],
ActionsMapped = {
[Name in keyof Actions]: (
...args: GetRestParams<Actions[Name]>
) => Promise<Awaited<ReturnType<Actions[Name]>>>
},
>(
module: Module,
): ActionsMapped => {
const store = useRootStore()
const namespace = module.name
return _getActions(module, store, namespace)
}
export const useGetters = <
Module extends GenericModule,
Getters = Module['getters'],
GettersMapped = {
[Name in keyof Getters]: ComputedRef<ReturnType<Getters[Name]>>
},
>(
module: Module,
): GettersMapped => {
const store = useRootStore()
const namespace = module.name
return _getGetters(module, store, namespace)
}
export const createVuexModule = <
State extends Record<string, unknown>,
Getters extends GetterTree<State, RootState>,
Actions extends ActionTree<State, RootState>,
Mutations extends MutationTree<State>,
>(
name: string,
{
state,
mutations,
actions,
getters,
}: {
state: State
mutations: Mutations
actions: Actions
getters: Getters
},
): {
name: string
state: State
mutations: Mutations
actions: Actions
getters: Getters
} => {
const structure = { state, mutations, actions, getters }
const moduleStructure = { namespaced: true, ...structure }
const hasModule = store.hasModule(name)
if (!hasModule) {
store.registerModule(name, moduleStructure)
}
if (hasModule && module.hot) {
store.hotUpdate({ modules: { [name]: moduleStructure } })
}
return { name, ...structure }
}
@hendrysadrak
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment