-
-
Save soerenmartius/ad62ad59b991c99983a4e495bf6acb04 to your computer and use it in GitHub Desktop.
import { | |
ActionContext, | |
ActionTree, | |
GetterTree, | |
MutationTree, | |
Module, | |
Store as VuexStore, | |
CommitOptions, | |
DispatchOptions, | |
} from 'vuex' | |
import { State as RootState } from '@/store' | |
// Declare state | |
export type State = { | |
isAuthenticated: boolean | |
} | |
// Create initial state | |
const state: State = { | |
isAuthenticated: false, | |
} | |
// mutations enums | |
export enum MutationTypes { | |
SET_USER_AUTHENTICATED = 'SET_USER_AUTHENTICATED', | |
} | |
// Mutation contracts | |
export type Mutations<S = State> = { | |
[MutationTypes.SET_USER_AUTHENTICATED](state: S): void | |
} | |
// Define mutations | |
const mutations: MutationTree<State> & Mutations = { | |
[MutationTypes.SET_USER_AUTHENTICATED](state: State) { | |
state.isAuthenticated = true | |
}, | |
} | |
// Action enums | |
export enum ActionTypes { | |
SIGNIN = 'SIGNIN', | |
} | |
// Actions context | |
type AugmentedActionContext = { | |
commit<K extends keyof Mutations>( | |
key: K, | |
payload: Parameters<Mutations[K]>[1], | |
): ReturnType<Mutations[K]> | |
} & Omit<ActionContext<State, RootState>, 'commit'> | |
// Actions contracts | |
export interface Actions { | |
[ActionTypes.SIGNIN]( | |
{ commit }: AugmentedActionContext, | |
payload: { username: string; password: string }, | |
): void | |
} | |
// Define actions | |
export const actions: ActionTree<State, RootState> & Actions = { | |
async [ActionTypes.SIGNIN]( | |
{ commit }, | |
payload: { username: string; password: string }, | |
) { | |
try { | |
// some logic that logs a user in | |
} catch (err) { | |
// some error handling logic | |
} | |
}, | |
} | |
// getters types | |
export type Getters = { | |
isAuthenticated(state: State): boolean | |
} | |
// getters | |
export const getters: GetterTree<State, RootState> & Getters = { | |
isAuthenticated: (state) => { | |
return state.isAuthenticated | |
}, | |
} | |
//setup store type | |
export 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]> | |
} | |
export const AuthModule: Module<State, RootState> = { | |
state, | |
mutations, | |
actions, | |
getters, | |
// Namespacing Vuex modules is tricky and hard to type check with typescript. | |
// Instead of namespacing, we could create our own namespacing mechanism by | |
// prefixing the value of the TypeScript enum with the namespace, e.g. | |
// enum TodoActions { | |
// AddTodo = 'TODO__ADD_TODO' | |
// } | |
// namespaced: true, | |
} |
import { | |
ActionContext, | |
ActionTree, | |
GetterTree, | |
Store as VuexStore, | |
CommitOptions, | |
DispatchOptions, | |
MutationTree, | |
Module, | |
} from 'vuex' | |
import { State as RootState } from '@/store' | |
import DomainService, { ResponseDomain as Domain } from '@/services/domain' | |
import RedirectionService, { | |
ResponseRedirection as Redirection, | |
} from '@/services/redirection' | |
// Declare state | |
export type State = { | |
domains: Domain[] | |
redirections: Redirection[] | |
} | |
// Create initial state | |
const state: State = { | |
domains: [], | |
redirections: [], | |
} | |
// Mutations enums | |
export enum MutationTypes { | |
SET_DOMAINS = 'SET_DOMAINS', | |
SET_REDIRECTIONS = 'SET_REDIRECTIONS', | |
} | |
// Mutation contracts | |
export type Mutations<S = State> = { | |
[MutationTypes.SET_DOMAINS](state: S, domains: Domain[]): void | |
[MutationTypes.SET_REDIRECTIONS](state: S, redirections: Redirection[]): void | |
} | |
// Define mutations | |
const mutations: MutationTree<State> & Mutations = { | |
[MutationTypes.SET_DOMAINS](state: State, domains: Domain[]) { | |
state.domains = domains | |
}, | |
[MutationTypes.SET_REDIRECTIONS](state: State, redirections: Redirection[]) { | |
state.redirections = redirections | |
}, | |
} | |
// Action enums | |
export enum ActionTypes { | |
FETCH_DOMAINS = 'FETCH_DOMAINS', | |
FETCH_REDIRECTIONS = 'FETCH_REDIRECTIONS', | |
} | |
// Actions context | |
type AugmentedActionContext = { | |
commit<K extends keyof Mutations>( | |
key: K, | |
payload: Parameters<Mutations[K]>[1], | |
): ReturnType<Mutations[K]> | |
} & Omit<ActionContext<State, RootState>, 'commit'> | |
// Actions contracts | |
export interface Actions { | |
[ActionTypes.FETCH_DOMAINS]( | |
{ commit }: AugmentedActionContext, | |
teamId: string, | |
): void | |
[ActionTypes.FETCH_REDIRECTIONS]( | |
{ commit }: AugmentedActionContext, | |
payload: { teamId: string; domainName: string }, | |
): void | |
} | |
// Define actions | |
export const actions: ActionTree<State, RootState> & Actions = { | |
async [ActionTypes.FETCH_DOMAINS]({ commit }, teamId: string) { | |
// As this is just an example, a Service Implementation is out of scope. | |
// A service in my case is basically an wrapper for a certain API. | |
const domainService = new DomainService() | |
const domains = await domainService.getDomains(teamId) | |
commit(MutationTypes.SET_DOMAINS, domains) | |
}, | |
async [ActionTypes.FETCH_REDIRECTIONS]( | |
{ commit }, | |
payload: { teamId: string; domainName: string }, | |
) { | |
const redirectionService = new RedirectionService() | |
const { teamId, domainName } = payload | |
let redirections: Redirection[] = [] | |
redirections = redirections.concat( | |
await redirectionService.getRedirections(teamId, domainName), | |
) | |
commit(MutationTypes.SET_REDIRECTIONS, redirections) | |
}, | |
} | |
// Getters types | |
export type Getters = { | |
getDomains(state: State): Domain[] | |
getRedirections(state: State): Redirection[] | |
} | |
// Getters | |
export const getters: GetterTree<State, RootState> & Getters = { | |
getDomains: (state) => { | |
return state.domains | |
}, | |
getRedirections: (state) => { | |
return state.redirections | |
}, | |
} | |
// Setup store type | |
export 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]> | |
} | |
export const DomainModule: Module<State, RootState> = { | |
state, | |
mutations, | |
actions, | |
getters, | |
} |
import { createStore, createLogger } from 'vuex' | |
import { | |
AuthModule, | |
Store as AuthStore, | |
State as AuthState, | |
} from '@/modules/auth/store' | |
import { | |
DomainModule, | |
Store as DomainStore, | |
State as DomainState, | |
} from '@/modules/domain/store' | |
export type State = { | |
auth: AuthState | |
domain: DomainState | |
} | |
export type Store = AuthStore<Pick<State, 'auth'>> & | |
DomainStore<Pick<State, 'domain'>> | |
export const store = createStore({ | |
plugins: | |
process.env.NODE_ENV === 'production' | |
? [] | |
: [createLogger()], | |
modules: { AuthModule, DomainModule }, | |
}) | |
export function useStore(): Store { | |
return store as Store | |
} | |
export default store |
Thanks! But it seems not possible to add some local state to the root state. Do you know how to achieve that?
yes sure, i can provide a new refactored example in a couple of days.
Just wanted to say thanks for this. There were a few longer tutorials that seemed to be quite convoluted but this got me right where I wanted
Can it be simplified
My getters seem to be throwing an exception and I'm not really sure why. Any suggestions?
EDIT
This seems to be an issue with vuex-multi-tab-state
Will be there some option for named modules? I can't make the state accessible thru namespaced module.
I haven't implemented namespacing yet and meanwhile stopped working with Vuex as it's not really fully typed itself. It might be a good idea to wait for Vuex 5 or even question if you need Vuex in the first place.
this looks extremely complicated. 🙄
Good to see that even the author has abandoned his 'solution'
Where Vue 3 is all about Typescript, Vuex 4 definitely missed that. Their (almost) only argument for using Vuex is it's great logging. That's mainly needed because of the poor implementation of their store depending on name string references.
Not sure what Vuex 5 will bring but they have to hurry or everyone will have found/created an alternative.
Just recently Evan You confirmed in Twitter that Pinia is Vuex 5.
Okay, that was a useful tip
Thanks! But it seems not possible to add some local state to the root state. Do you know how to achieve that?