Strongly-typed Vuex Store in NuxtJS
Vuex store code can be quite verbose, especially with wrappers for type-safety in TypeScript. A lot of that plumbing can be avoided with the vuex-module-decorators package. There's some extra configuration required to get it working in NuxtJS.
The core module definition for NuxtJS is the same as in plain Vue.js and is well documented:
The store module is defined as a class extending
VuexModule
with@Module
decorator.@Module export default class SampleModule extends VuexModule { // ... }
The state consists of class properties.
count = 0;
Getters are implemented as getter functions.
get isDefault(): boolean { return this.count === 0 }
Mutations are methods with the
@Mutation
decorator. Only one parameter is supported. For more, they should be passed as a single payload object. Methods must be synchronous and shouldn't return a value@Mutation increment(): void { this.count++ }
Actions are methods with the
@Action
decorator. They also support only a single parameter like mutations. They can be asynchronous and should return a promise. TherawError
parameter should be set if the method is supposed to throw errors so that those errors aren't wrapped.@Action({ rawError: true }) incrementAsync(): Promise<void> { return new Promise<void>((resolve) => { setTimeout(() => { this.increment() resolve() }, 100) }) }
To use the module from NuxtJS it should be namespaced and dynamic. This can be configured with @Module
decorator parameters:
@Module({ name: 'sample', stateFactory: true, namespaced: true })
export default class SampleModule extends VuexModule {
// ...
}
When using NuxtJS in SSR mode, the suggested pattern in the vuex-module-decorators documentation shouldn't be implemented because it introduces singleton store variables which will cause the state to be shared between requests. Instead, the guidance for SSR in the same documentation should be followed.
The following helper function can make that code a bit simpler:
export function getSampleModule(store: Store<any>): SampleModule {
return getModule(SampleModule, store);
}
The exported function can then be used instead of the plain getModule
function to access the store from elsewhere in the application, e.g. a page or a component. All module members (state, getters, mutations, and actions) are fully typed:
import Vue from 'vue';
import Component from 'vue-class-component';
import { getSampleModule } from '~/store';
@Component
export default class IndexPage extends Vue {
get count(): number {
return getSampleModule(this.$store).count;
}
get isDefault(): boolean {
return getSampleModule(this.$store).isDefault;
}
increment() {
getSampleModule(this.$store).increment();
}
incrementAsync() {
getSampleModule(this.$store).incrementAsync();
}
}
A full working example is in my GitHub repository.
The vuex-module-decorators package is a great choice when using TypeScript as it allows strongly-typed access to the Vuex store with minimum additional plumbing code. When defined and used correctly, such modules can also be used with NuxtJS, even in SSR mode.