Vuexを使用する際にvuex-module-decoratorsを用いて補完を効かせる

# はじめに
エンジニアとして働き始めて4ヶ月経ち5ヶ月目に突入したのですが、自分の技術力のなさに絶望しながら日々奮闘しています!

そんな僕が以前から気になっていたVuex+Typescript環境下でインテリセンスが効きにくいと感じていた問題を
解決してくれる`vuex-module-decorators`というものを使用したのでメモとして残しておきます。

https://github.com/championswimmer/vuex-module-decorators


# install
```
yarn add -D vuex-module-decorators

npm install -D vuex-module-decorators 
```

# Setup
`~/utils/store-accessors.ts`ではinstallしたモジュールをインポートし、storeに登録します。
また、新しいstoreが出来次第このファイルに追加していきます。

```typescript
// ~/utils/store-accessors.ts
import { Store } from "vuex";
import { getModule } from "vuex-module-decorators";
import User from "~/store/users";

// 新しいstoreが出来次第以下に追加していく
let usersStore: User;
// let exampleStore: Example

function initialiseStores(store: Store<any>): void {
  usersStore = getModule(User, store);
}

export {
  initialiseStores,
  usersStore,
};

```

`~/store/index.ts`ではコンポーネントからインポートできるように`store-accessor.ts`のstoreをexportしています。
またstoreを初期化しています。

```typescript
// ~/store/index.ts
import { Store } from 'vuex'
import { initialiseStores } from '~/utils/store-accessor'
const initializer = (store: Store<any>) => initialiseStores(store) //storeの初期化
export const plugins = [initializer]
export * from '~/utils/store-accessor' //storeをexportする
```

# Storeの作成
こちらはstoreの全体イメージです。
MutationやActionなどは基本的にデコレーターを使用し作成します。
getterとstateはデコレーターを使用しないのは謎ですが、
そんなことはさておき、以下で順を追って解説をしていきます。

```typescript
// ~/store/users.ts
import { Module, VuexModule, Mutation, Action } from "vuex-module-decorators";
import { $axios } from "~/plugins/api";

export type UserType = {
  name: string;
  age: number;
};

@Module({
  name: "users",
  stateFactory: true,
  namespaced: true,
})
export default class User extends VuexModule {
  private users: UserType[] = [];

  public get getUsers() {
    return this.users;
  }

  @Mutation
  public addUsers(user: UserType) {
    return this.users.push(user);
  }

  @Mutation 
  public setUsers(users: UserType[]) {
    return this.users = users;
  }

  @Action
  public async fetchUsersData() {
    const { data } = await $axios.get<UserType[]>("/api/users/");
    this.setUsers(data);
  }
}

```

# 解説
## 必要なmoduleをインポートする
`$axios`は`Action`でapiを叩く際に使用するのでインポートしています。

```typescript
import { Module, VuexModule, Mutation, Action } from "vuex-module-decorators";
import { $axios } from "~/plugins/api";

```

## 型定義
ここでは直接storeファイルに記述していますが、typesフォルダに切り分けて記述した方が綺麗かも。

```typescript
export type UserType = {
  name: string;
  age: number;
};
```

## Moduleを定義する
- nameにパスを指定。
- `stateFactory: true` とすることで、NuxtがモジュールモードでStoreを作ってくれます。
- namespced: trueとすることで、名前空間が適用されます

```typescript
@Module({
  name: "users",
  stateFactory: true,
  namespaced: true,
})
```

## state作成
stateは基本的にクラス内からしかアクセスしないため、privateで定義します。

```typescript
private users: UserType[] = [];
```

## getter
usersの値を返すgetterです。
stateへはクラス内であれば`this`でアクセスできます。

```typescript

  public get getUsers() {
    return this.users;
  }
```

## Mutation
usersのデータをセットするMutationです。
ここでprivateで定義しているのは、ActionからのみMutationを実行するためです。
基本的にMutationはprivateで定義するのがベターかもしれません。

```typescript
 @Mutation 
  private setUsers(users: UserType[]) {
    return this.users = users;
  }
```

## Action
以下のような非同期処理を実行する際にはAcitonを使用します。
取得したuserのdataを先ほどMutationで定義したsetUsers関数の引数に渡しています。

```typescript
 @Action
  public async fetchUsersData() {
    const { data } = await $axios.get<UserType[]>("/api/users/");
    this.setUsers(data);
  }
```

## コンポーネント内でActionを実行する

```typescript
import { usersStore } from "@/store";

const store = usersStore;
store.fetchUsersData(); //Actionを実行。
```

これでコンポーネント内でActionを実行することができました。
補完も効いてて最高です。
![スクリーンショット 2021-12-13 1.36.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375107/bf8cf815-37df-e7c9-2299-90bd6c2a1c7b.png)

# おわりに
以前からvuexを使用する際に補完が効かなかったりしてめんどくさいなと思っていたので
今後もVuexを使用する際はこちらを採用しようと思います。
もうcommitやdispatchとはおさらばなのかもしれませんね。