Skip to content

Instantly share code, notes, and snippets.

@luna-koury
Last active April 6, 2022 13:32
Show Gist options
  • Save luna-koury/ad3e2a2a62533aa590784a0eff2bef17 to your computer and use it in GitHub Desktop.
Save luna-koury/ad3e2a2a62533aa590784a0eff2bef17 to your computer and use it in GitHub Desktop.
Vuex 4 - Boilerplate Typescript

Vuex 4 boilerplate using Vue3 and typed modules with TypeScript

With Modules

📦src
 ┣ 📂store
 ┃ ┣ 📂modules
 ┃ ┃ ┗ 📂generic_module
 ┃ ┃ ┃ ┣ 📜actions.ts
 ┃ ┃ ┃ ┣ 📜getters.ts
 ┃ ┃ ┃ ┣ 📜index.ts
 ┃ ┃ ┃ ┗ 📜mutations.ts
 ┃ ┗ 📜index.ts > *root_index.ts*
import { ActionContext, ActionTree } from "vuex";
import { Mutations, MutationTypes } from "./mutations";
import { State } from "./index";
import { RootState } from "@/store";
//(C) - Ações
// [C.1] inserir a definição da ação x no enum
// ==> { setX = "NOMEMODULO__SET_ X" }
export enum ActionTypes {
setValue = "GENERIC__SET_VALUE",
}
// !!! AUGUMENTED ACTION CONTEXT !!!
type AugmentedActionContext = {
commit<K extends keyof Mutations>(
key: K,
payload: Parameters<Mutations[K]>[1]
): ReturnType<Mutations[K]>;
} & Omit<ActionContext<State, RootState>, "commit">;
// [C.2] definir o tipo da ação setX
// ==> [ActionTypes.setX](
// ==> { commit }: AugmentedActionContext,
// ==> payload: TIPO_DE_X
// ==> ): void;
export interface Actions {
[ActionTypes.setValue](
{ commit }: AugmentedActionContext,
payload: any
): void;
}
// [C.3] declara a ação setX
// [ActionTypes.setX]({ commit }, payload) {
// commit(MutationTypes.X, payload);
// },
export const actions: ActionTree<State, RootState> & Actions = {
[ActionTypes.setValue]({ commit }, payload) {
commit(MutationTypes.VALUE, payload);
},
};
<template>
<input type="text" name="test" id="test" v-model="data" />
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import useStore from "path/to/store/index";
import { ActionTypes as ModuleA } from "path/to/module/index";
export default defineComponent({
name: "name",
components: {},
props: {},
setup() {
const data = ref("");
// declare useStore inside composition API setup()
const store = useStore();
// this will be acording with the module's action.ts
store.dispatch(ModuleA.setData, data.value);
// this will be acording with the module's getters.ts
console.log(store.getters.data);
return { data };
},
});
</script>
import { GetterTree } from "vuex";
import { State } from "./index";
import { RootState } from "@/store";
// (D) - Getters
// [D.1] - Define o tipo do getter x
// => x(state: S): TIPO_DE_X;
export type Getters<S = State> = {
value(state: S): any;
};
// [D.2] - Declara o getter x
// => x: (state) => {
// => return state.x;
// => },
export const getters: GetterTree<State, RootState> & Getters = {
value: (state) => {
return state.value;
},
};
import { getters, Getters } from "./getters";
import { mutations, Mutations } from "./mutations";
import { actions, Actions, ActionTypes } from "./actions";
import {
Store as VuexStore,
Module,
CommitOptions,
DispatchOptions,
} from "vuex";
import { RootState } from "@/store";
// PARA ALTERAR A STORE
// (A) - Alterar o state ./
// (B) - Alterar as mutações ./mutations.ts
// (C) - Alterar as ações ./actions.ts
// (D) - Alterar os getters ./getters.ts
// !!! não mecher nos snipets de código com comentário em maiusculo !!!
//[A.1] inclui valor na interface do state
// ==> x: TIPO_DE_X
interface State {
value: any;
}
//[A.2] declara o valor no objeto do state
// ==> x: VALOR_DE_X
const state: State = {
value: "",
};
const generic_module: Module<State, RootState> = {
state,
mutations,
actions,
getters,
};
export { State, ActionTypes, Store };
export default generic_module;
// !!! CONFIGURA TIPAGEM DA STORE !!!
type Store<S = State> = Omit<
VuexStore<S>,
"commit" | "getters" | "dispatch"
> & {
commit<K extends keyof Mutations, P extends Parameters<Mutations[K]>[1]>(
key: K,
payload: P,
options?: CommitOptions
): ReturnType<Mutations[K]>;
} & {
getters: {
[K in keyof Getters]: ReturnType<Getters[K]>;
};
} & {
dispatch<K extends keyof Actions>(
key: K,
payload: Parameters<Actions[K]>[1],
options?: DispatchOptions
): ReturnType<Actions[K]>;
};
// this should be in your main.ts file
import { store, key } from "path/to/store/index";
const app = createApp(App);
app.use(store, key);
import { MutationTree } from "vuex";
import { State } from "./index";
// (B) - Mutações
// [B.1] inserir a definição da mutação X no enum
// ==> { X = "SET_ X" }
export enum MutationTypes {
VALUE = "SET_VALUE",
}
// [B.2] definir o tipo da mutação X
// ==> [MutationTypes.X](state: S, payload: TIPO_DE_X): void;
export type Mutations<S = State> = {
[MutationTypes.VALUE](state: S, payload: any): void;
};
// [B.3] declarar a mutação X
// ==> [MutationTypes.X](state, payload) {
// ==> state.x = payload;
// ==> },
export const mutations: MutationTree<State> & Mutations = {
[MutationTypes.VALUE](state, payload) {
state.value = payload;
},
};
import { InjectionKey } from "vue";
import {
createStore,
Store as VuexStore,
useStore as baseUseStore,
} from "vuex";
import generic, {
State as GenericState,
Store as GenericStore,
} from "./modules/__generic_module";
import other, {
State as OtherState,
Store as OtherStore,
} from "./modules/other_module";
// define tipos pro state da store
export interface RootState {
debitos: GenericState;
/* other: OtherState; */
}
export type RootStore = GenericStore<Pick<RootState, "debitos">>;
// & OtherStore<Pick<RootState, "other">> &
// define injection key
export const key: InjectionKey<VuexStore<RootState>> = Symbol();
export const store = createStore<RootState>({
modules: {
generic,
// other
},
});
// o usar `import useStore from '@store'`
export default function useStore(): RootStore {
return baseUseStore(key);
}
@echevarriandre
Copy link

I'd like to mention there is a typo on the component.vue gist, if I'm not mistaken.

I believe it should be setValue:

store.dispatch(ModuleA.setValue, data.value);

Instead of setData:

store.dispatch(ModuleA.setData, data.value);

@lcjltj
Copy link

lcjltj commented Jul 24, 2021

Hello, I am using vuex in the same way as above. If namespaced is not used, the state and action of the same variable method is impossible, so the value of namespaced is used as true. Can't I use the above method when using namespaced??

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