📜 ⬆️ ⬇️

Powerful module for typing Vuex

The motive for writing this article was another article on the topic of typing Vue and, accordingly, Vuex . To my surprise, I didn’t find there any mention of the module, which, in my opinion, is the best of its kind "typifier" Vuex. A search on Habr, and indeed on Runet (in fact, and in English-language sources, it is not easy to immediately find any references), alas, did not give any results. This article is not a detailed analysis and a multipage manual on the use and configuration, but rather a way to share with you, dear Vue-ninja, a tool that does an excellent job with its task.

vuex-smart-module


Who has no time at all: Github .

The main purpose of the module, as you managed to guess, is the full-size coverage of the Vuex storage types. Both internally and directly in the components themselves. The module is written by the main contributor ( @ktsn ) of the Vuex and vue-class-component libraries.

Water


Frankly, my path to Typescript began quite recently, incl. and with such things as decorators, so I can not compare this library with other counterparts. My attempts to customize and use other tools (for example, vuex-module-decorators ) led me to various problems that in the end somehow did not allow me to realize what I needed (or I just, as they say, did not know how to prepare them). I was very lucky with the vuex-smart-module - the library appeared at the very moment when I translated the project (and the repository) into Typescript. Now everything works fine, and the code is pleasing to the eye.
')

Examples


The library, in my opinion, has good documentation that covers all possible cases that you will encounter (and if not, then you can find everything else in the tests, non-trivial). However, in order to somehow dilute the article with code, I will give basic examples of connection and use, a couple of examples of “life”, as well as how it works in conjunction with decorators (there is a nuance).

Module creation


A module is created using classes. To some, this may seem strangely frightening, but believe me, you get used to it quickly.

store / root.ts

//    import { Getters, Mutations, Actions, Module } from 'vuex-smart-module' //  class RootState { count = 1 } //  //      RootState class RootGetters extends Getters<RootState> { get double() { //      `state` return this.state.count * 2 } get triple() { //       `getters` return this.getters.double + this.state.count } } //  //     ,     RootState class RootMutations extends Mutations<RootState> { increment(payload: number) { //       `state` this.state.count += payload } } //  //     //    ,       ,      class RootActions extends Actions< RootState, RootGetters, RootMutations, RootActions > { incrementAsync(payload: { amount: number; interval: number }) { //      `state`, `getters`, `commit`  `dispatch` return new Promise(resolve => { setTimeout(() => { this.commit('increment', payload.amount) }, payload.interval) }) } } //   export default new Module({ state: RootState, getters: RootGetters, mutations: RootMutations, actions: RootActions }) 

Connection


/store/index.ts

 import Vue from 'vue' import * as Vuex from 'vuex' import { createStore } from 'vuex-smart-module' import RootStore from './root' Vue.use(Vuex) export const store = createStore( RootStore, { strict: process.env.NODE_ENV !== 'production' } ) 

Modules


Connecting modules in the same way as in normal Vuex. They need to be specified in the modules property of the RootStore:

 import FooStore from './modules/foo' /* … */ export default new Module({ state: RootState, getters: RootGetters, mutations: RootMutations, actions: RootActions, modules: { FooStore } }) 

Use inside component


You can use the store either through the global property this. $ Store, or through the mapping, which is in many ways similar to the one in Vuex:

 import Vue from 'vue' //    (      ) // import FooStore from '@/store/modules/foo' import RootStore from '@/store/root' export default Vue.extend({ computed: RootStore.mapGetters(['double']), methods: RootStore.mapActions({ incAsync: 'incrementAsync' }), created() { console.log(this.double) this.incAsync(undefined) } }) 


Typing


An example of commit and dispatch typing:
 import { categories } from '@/api' export type Category { attributes: { hasPrice: boolean; icon: string lvl: number name: string slug: string }; id: number } export interface IParams { city_id: number } class AppState { categories: Category[] = [] } /* ... */ class AppMutations extends Mutations<AppState> { setCategories(categories: Category[]) { this.state.categories = categories } } class AppActions extends Actions< AppState, AppGetters, AppMutations, AppActions > { async getCategories({params}: {params: IParams}): Promise<Category[]> { return categories.get({params}).then( ({ data }: { data: Category[] }) => { this.commit("setCategories", data) return data } ) } } 


Receptions


Connection using decorators ( vue-property-decorator )


 import { Vue, Component } from "vue-property-decorator" //    (      ) // import FooStore from '@/store/modules/foo' import RootStore from "@/store/root" //  ,   ,      Typescript,     : const Mappers = Vue.extend({ computed: { ...RootStore.mapGetters(["double"]) }, methods: { ...RootStore.mapActions({ incAsync: 'incrementAsync' }) } }); @Component export default class MyApp extends Mappers { created() { console.log(this.double) this.incAsync(undefined) } } 

Using the module inside the module


/store/module/bar.ts

 import { Store } from 'vuex' import { Getters, Actions, Module, Context } from 'vuex-smart-module' //    import FooStore from './foo' /* … */ class BarGetters extends Getters { //   foo!: Context<typeof FooStore>; //     $init(store: Store<any>): void { //     this.foo = FooStore.context(store) } get excited(): string { return this.foo.state.value + '!' // -> hello! } } /* … */ 

Reset storage


Sometimes it may be necessary to reset the repository to its default values, which is quite simple:

 class FooState { /* ... */ } class FooMutations extends Mutations<FooState> { reset () { const s = new FooState() Object.keys(s).forEach(key => { this.state[key] = s[key] }) } } 

The final


I hope you were interested, well, or at least you learned about this library. Who knows, maybe starting from the next project (and maybe the current refactoring is just around the corner?), You will start, like me, to use vuex-smart-module (or Typescript in general)? Personally, my transition to Typescript was quite painful (for 1.5-2 years I started trying to switch to it at least 3-4 times, but every time I faced some problems, misunderstanding. I was often haunted by the feeling that developing Typescript takes 2-3 times more time than before, because now you can’t just “quickly jot down.” But once, stepping on the “bright side of static typing,” I felt the power of the types and how they ultimately allow to speed up the development process, which is equally important, to debug the code (perhaps in the same 2 3 times), as well as facilitate its further support.

PS Do not forget to put a star on this module. :)

UPD: I offer my deepest apologies to a respected user, whom I may have offended by my post and forced him to merge my karma. I would be extremely grateful to you, Mr. (-J), if you write in a personal (or you can in the commentary on the article) - what mistake did I make to avoid it in the future.

UPD 2: Added a typing example.

Thanks
In conclusion, I want to thank my beloved wife for her patience, the cat for the pleasant rumble next to the table, the neighbors for the silence and, of course, you for your attention!

Source: https://habr.com/ru/post/459050/


All Articles