Search code examples
typescriptnuxt.jsvuexvuex-modulesvuex-module-decorators

How we can access to Vuex store instance inside store module file in vuex-module-decorators + Nuxt TypeScript?


There are many possible ways to construct your modules.

vuex-module-decorators official readme

One of the most popular approaches is vuex-module-decorators.

Nuxt TypeScript official documentation

Well, where at least two approaches has been introduced? The documentations are introducing just one approach and most articles are refers to it.

The official approach listing

~/store/index.ts

import { Store } from 'vuex'
import { initialiseStores } from '~/utils/store-accessor'
const initializer = (store: Store<any>) => initialiseStores(store)
export const plugins = [initializer]
export * from '~/utils/store-accessor'

~/utils/store-accessor.ts

import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import example from '~/store/example'

let exampleStore: example

function initialiseStores(store: Store<any>): void {
  exampleStore = getModule(example, store)
}

export { initialiseStores, exampleStore }

Why I don't accept it

  1. I don't understand what we doing. Although the code volume is small, there are a lot of not obvious things. For example: Why we need to define the plugins constant? Why we need to export it? How and where we using this export?
  2. I don't understand why we need to the acrobatics such as. If there are the other approaches, it must the more intuitive solution.
  3. I don't understand why we should to follow to this approach, not other one.
  4. It's not enough type-safe. 'any' has been used.

What I want to do

If it's possible, I want to use the 'vuex-module-decorators' as usual, or, at least understand what going on and develop more clean solution.

The standard usage of dynamic modules in 'vuex-module-decorators' is:

import store from "@store";
import { Module, VuexModule } from "vuex-module-decorators";

@Module({
  name: "PRODUCTS_MANAGER",
  dynamic: true,
  namespaced: true,
  store
})
class ProductsManagerStoreModule extends VuexModule {}

But how we get the store instance in Nuxt TypeScript application? The Nuxt takes out the vuex store from us (same as routing and webpack control) to make the development simply, but here is the reverse effect.

Try to use the vuex store as usual

It's interesting, but it works in development environment, but not in production.

store/index.ts

import Vuex, { Store } from "vuex";


export const store: Store<unknown> = new Vuex.Store({});


export default store;

pages/index.vue

import { Component, Vue } from "nuxt-property-decorator"
import { getModule } from "nuxt-property-decorator";
import TestStoreModule from "./../Store/TestStoreModule";


@Component
export default class extends Vue {

  private readonly title: string = "It Works!";

  private mounted(): void {
    console.log("=========");
    console.log(getModule(TestStoreModule).currentCount);
  }
}

enter image description here

store/TestStoreModule.ts

import { Module, VuexModule, Mutation } from "vuex-module-decorators";
import store from "./index";

@Module({
  name: "TestStoreModule",
  namespaced: true,
  dynamic: true,
  store
})
export default class TestStoreModule extends VuexModule {

  private count: number = 0


  @Mutation
  public increment(delta: number): void {
    this.count += delta
  }
  @Mutation
  public decrement(delta: number): void {
    this.count -= delta
  }

  public get currentCount(): number {
    return this.count;
  }
}

But after production build, I have below error displaying in console:

 ERROR  [nuxt] store/index.ts should export a method that returns a Vuex instance. 

🌎 Application in above state (GitHub repository) (The project structure a little bit different, I am sorry).

I tried to fix it as has been told. Now, store/index.ts is:

// config.rawError = true;
/* 〔 see 〕 https://develop365.gitlab.io/nuxtjs-2.1.0-doc/pt-BR/guide/vuex-store/ */
export default function createStore(): Store<unknown> {
  return new Vuex.Store({});
}

The TestStoreModule can not refer to store now (if can, how?).

@Module({
  name: "TestStoreModule",
  namespaced: true,
  stateFactory: true
})
export default class TestStoreModule extends VuexModule { /* ... */ }

In the component, tried to access to store module as:

console.log(getModule(TestStoreModule, this.$store).currentCount);

The output is:

enter image description here

Seems like the 'this' problem. If we output the console.log(getModule(TestStoreModule, this.$store)); and call the currentCount from Chrome console:

enter image description here

we get the error:

Exception: TypeError: Cannot read property 'count' of undefined at Object.get
(webpack-internal:///./node_modules/vuex-module-decorators/dist/esm/index.js:165:36) 
at Object.r (<anonymous>:1:83)

🌎 Application in above state (GitHub repository)


Solution

  • When I asked this question, I did not know that it's possible to use the dynamic modules in Nuxt as in plain Vue application. The dynamic modules gives the cleanest syntax and makes the effect like each module is independent. No need in hacks like store-accessor.ts; no need to gather all modules is single directory. This approach is my select and recommendation.

    The question Dynamic vuex store modules with NuxtJS and vuex-module-decorators covers the setup of vuex-module-decorators for Nuxt.

    In this way, answering to current question, in store/index.ts we can create the store instance, import it and use in each dynamic module:

    import Vue from "vue";
    import Vuex, { Store } from "vuex";
    
    
    Vue.use(Vuex);
    
    export const store: Store<unknown> = new Vuex.Store<unknown>({});