queue-stack / qms-nuxt-ts

Queue Management System (POC) created with (Nuxt Typescript/vuetify/firebase) [WIP]
MIT License
1 stars 0 forks source link

Get the easiest way to use vuex decorators with nuxt-TS #17

Open sniperadmin opened 4 years ago

sniperadmin commented 4 years ago

Note

All possible trials to use vuex decorators will be recorded on here.

Approach 1 (crashes with nuxt [ Reference Error: Cannot access 'store' before initialization ])

@/store/index.ts

import Vuex from 'vuex'
import Counter from './counter'

export const store = new Vuex.Store({  
  modules: {
    Counter
  }
})

@/store/modules/counter

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

@Module({
  name: 'counter',
  stateFactory: true,
  namespaced: true,
  store,
})
export default class Counter extends VuexModule {
  public counter: number = 0

  get getCounter() {
    return this.counter
  }

  @Mutation
  INCREMENT_COUNTER() {
    this.counter++
  }

  @Mutation
  DECR_COUNTER() {
    this.counter--
  }

  @Action
  increment({ commit }) {
    commit('INCREMENT_COUNTER')
  }

  @Action
  decr({ commit }) {
    commit('DECR_COUNTER')
  }
}

in the vue component:

<span>{{ $store.getters['counter/getCounter'] }}</span>

Note: This approach works with Vue

The rest of approaches will be documented on here

sniperadmin commented 4 years ago

Approach2 (Works with nuxt-ts so far)

  1. Based on the follwing created module: @/store/counter.ts
    
    import { Module, Action, Mutation, VuexModule } from 'vuex-module-decorators'

@Module({ name: 'counter', stateFactory: true, namespaced: true, }) export default class Counter extends VuexModule { public counter: number = 0

get getCounter() { return this.counter }

@Mutation INCREMENT_COUNTER() { this.counter++ }

@Mutation DECR_COUNTER() { this.counter-- }

@Action increment({ commit }) { commit('INCREMENT_COUNTER') }

@Action decr({ commit }) { commit('DECR_COUNTER') } }

I created a store accessor:
`@/utils/store-accessor`
```ts
import { Store } from 'vuex'
import { getModule } from 'vuex-module-decorators'
import Counter from '@/store/counter'

let counterStore: Counter

function initialiseStores(store: Store<any>) {
  counterStore = getModule(Counter, store)
}

export { initialiseStores, counterStore }
  1. Then we use it at the index store: @/store/index.ts
    
    import { Store } from 'vuex'
    import { initialiseStores } from '@/utils/store-accessor'

const initializer = (store: Store) => initialiseStores(store)

export const plugins = [initializer]

export * from '@/utils/store-accessor'


3. In the vue component:
`example.vue`
```vue.js
import { counterStore } from '@/store'

@Component({})
export default class Index extends Vue {
  public testMethod() {
    console.log(counterStore.getCounter)
  }

  mounted() {
    this.testMethod()
  }
}

This approach works so far, but need to find a god way for testing it using jest...

iliyaZelenko commented 4 years ago

Approach2 (Works with nuxt-ts so far)

This shares your data between requests. I already experienced all the pain with this method. This is noticeable when there are many users.

sniperadmin commented 4 years ago

appreciate your contributions, I will check the open issues... this project is still a POC, so i won't get worried about users for now,.. but i need to continue, so far, If there is a better solution, it would be great

iliyaZelenko commented 4 years ago

I'm using this:

image

The point is to do this as a plugin and inject in this.$storeModules.

In fact, working with vuex-module-decorators for more than a year, I would not advise using the it, it creates quite a few restrictions.

sniperadmin commented 4 years ago

Didn't try that before, let me check... 🚀

sniperadmin commented 4 years ago

@iliyaZelenko ... Do I have to define a storeAppContextInterface ?

iliyaZelenko commented 4 years ago
import QuestionsStoreMod from '~/store/QuestionsStoreMod'
import AdvertisingStoreMod from '~/store/AdvertisingStoreMod'
import MainPageStoreMod from '~/store/MainPageStoreMod'
import PostStoreMod from '~/store/PostStoreMod'
import ToastsStoreMod from '~/store/ToastsStoreMod'
import AuthStoreMod from '~/store/AuthStoreMod'
import PostCommentsStoreMod from '~/store/PostCommentsStoreMod'
import DiaryStoreMod from '~/store/DiaryStoreMod'
import CommunityStoreMod from '~/store/CommunityStoreMod'
import ModerationStoreMod from '~/store/ModerationStoreMod'
import UIStoreMod from '~/store/UIStoreMod'
import MessengerStoreMod from '~/store/MessengerStoreMod'

export interface StoreAppContextInterface {
  auth: AuthStoreMod
  questions: QuestionsStoreMod
  advertising: AdvertisingStoreMod
  mainPage: MainPageStoreMod
  post: PostStoreMod
  postComments: PostCommentsStoreMod
  toasts: ToastsStoreMod
  diary: DiaryStoreMod
  community: CommunityStoreMod
  moderation: ModerationStoreMod
  UI: UIStoreMod
  messenger: MessengerStoreMod
}
sniperadmin commented 4 years ago

Tried to be simple:

// `@/plugins/store.ts`

import Counter from '@/store/counter'
import { getModule } from 'vuex-module-decorators';

interface StoreAppContextInterface {
  counter: Counter
}

export default (ctx, Inject) => {
  const storeModulesClasses = {
    counter: Counter
  }

  const storeModule = {} as StoreAppContextInterface

  for (const key in storeModulesClasses) {
    storeModule[key] = getModule(storeModulesClasses[key], ctx.store)

    Object.setPrototypeOf(storeModule[key], {
      appContext: ctx
    })
  }

  Inject('storeModules', storeModule)
  ctx.$storeModules = storeModule
}

in my store dir => there is only the module file counter.ts still it does not inject it properly

justinIs commented 4 years ago

This approach worked well for me. It's a bit nicer too IMO (you need to translate the page from Japanese): https://qiita.com/yoshinbo/items/70f109db7c3de4b4a99f

iliyaZelenko commented 4 years ago

@justinIs classic mode is deprecated

Image 2020-08-14 at 8 11 47 PM

olegstepura commented 3 years ago

Tried to be simple

Thanks a lot, @sniperadmin. Here is version with all the missing types:

// `@/plugins/store.ts`

import type { Context } from '@nuxt/types'
import type { Inject } from '@nuxt/types/app'
import { getModule } from 'vuex-module-decorators'
import ExampleModule from '@/store/example'

export interface StoreAppContextInterface {
  data: ExampleModule
}

declare module 'vue/types/vue' {
  // adds this.$storeModules inside Vue components
  interface Vue {
    $storeModules: StoreAppContextInterface
  }
}

export default (ctx: Context, inject: Inject): void => {
  const storeModulesClasses = {
    data: ExampleModule,
  }

  const storeModules = {} as StoreAppContextInterface
  const keys = Object.keys(storeModulesClasses) as Array<
    keyof typeof storeModulesClasses
  >

  keys.forEach((key: keyof typeof storeModulesClasses) => {
    storeModules[key] = getModule(storeModulesClasses[key], ctx.store)

    Object.setPrototypeOf(storeModules[key], {
      appContext: ctx,
    })
  })

  // inject doesn't inject in context but in context.app
  inject('storeModules', storeModules)
  // if there will be a need to access it directly, we can uncomment below
  // ctx.$storeModules = storeModules
}

UPD: sadly Object.setPrototypeOf does not actually work, store modules will not have access to appContext this way with vuex-module-decorators. It looks like instances of classes representing Vuex modules are then destructured and composed to some other objects internally loosing access to the previous this.