Vuexを理解する

# はじめに
最近仕事でVuexを理解する必要が出てきたので、自分の理解のためにメモしておきます。

# Vuexとは
VuexはVue.jsにおいて、データの受け渡しを便利してくれるライブラリ。
通常のVue.jsでは、データを他のコンポーネントに渡す際に`props`や`$emit`を使います。コンポーネント数が少ない場合はこれでも大丈夫なのですが、コンポーネント数が増えたり、階層が深くなると、素のVue.jsではデータの管理が難しくなってしまいます。

そんなデータ管理の難しさを解消してくれるのがVuexです。今回は以下の点について説明してあります。

- state
- getters
- mutations
- actions

# Vuexを触ってみる
## `state`を使ってみる
以下でプロジェクト作成、Vuexインストール後に[localhost:8080](http://localhost:8080/)にアクセスしてサンプル画面がでていることを確認します。
```terminal
% vue create vuex-demo
% cd vuex-demo
% npm install vuex
% npm run serve
```

まずは、Vuexを使うための設定を`main.js`に記述します。
```vuex-demo/src/main.js
import { createApp } from "vue";
import App from "./App.vue";
import store from '../store' // vuexを使う設定

const app = createApp(App)

app.use(store) //vuexを使う設定
app.mount('#app')
```
storeを使うための記述を`vuex-demo/store/index.js`に追記します。
```vuex-demo/store/index.js
import { createStore } from "vuex";

export default createStore({
    state: {
        message: 'Hello Vuex!'
    }
});
```
最後にvueファイルにstoreで定義したメッセージを呼び出してみます。
呼び出し方は`this.$store.state.~`です。
```vuex-demo/src/App.vue
<template>
  <div id="app">
    <h1>{{ this.$store.state.message }}</h1>
  </div>
</template>

<script>
export default {
  name: 'app',
}
</script>
```

store.stateにメッセージを記述することで、他のコンポーネントでも`message`を参照したいときは`App.vue`と同じように書けばよいのです。それを確認します。

`App.vue`ではなく`components`配下にファイルを作成し、`this.$store.state.message`を追記し、画面に同じメッセージが表示されることを確認します。
```vuex-demo/src/components/contens.vue
<template>
    <h2>{{ this.$store.state.message }}</h2>
</template>

<script>
export default ({
    name: 'sampleContents',
})
</script>
```
`App.vue`も追記をしていきます。
```vuex-demo/src/App.vue
<template>
  <div id="app">
    <h1>{{ this.$store.state.message }}</h1>
    <sampleContents></sampleContents>
  </div>
</template>

<script>
import sampleContents from './components/contents.vue'

export default {
  name: 'app',
  components: {
    sampleContents
  }
}
</script>
```
画面は以下のようになるはずです。stateファイルで同じ内容の`message`を参照できることがわかると思います。
![スクリーンショット 2022-07-24 16.14.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326068/765dd12d-fb6f-ef7a-ce7c-08330e1414f5.png)
## `getters`を使ってみる
storeの状態を算出するために使うのが`getters`です。下のように書くことで条件による絞り込みを行うことができます。`computed`的なことを実行することができるものです。

```vuex-demo/store/index.js
import { createStore } from "vuex";

export default createStore({
    state: {
        message: 'Hello Vuex!',
        // gettersのためにデータを追加
        todos: [
            {id: 1, text: 'Learn Vuex', done: true},
            {id: 2, text: 'Learn Vue.js', done: false},
            {id: 3, text: 'Learn Python', done: true},
            {id: 4, text: 'Learn Node.js', done: false},
            {id: 5, text: 'Learn Julia', done: true}
        ]
    },
    // gettersを追加！
    getters: {
        // 何の変哲ものない書き方？
        doneTodos (state) {
            return state.todos.filter(todo => todo.done)
        },
        // プロパティスタイルアクセスの書き方
        doneTodosCount (state, getters) {
            return getters.doneTodos.length
        },
        // メソッドスタイルアクセスの書き方
        getTodoById: (state) => (id) => {
            return state.todos.find(todo => todo.id === id);
        }
    }
});
```
呼び出し方は
```vuex-demo/src/components/contents.vue
<template>
    <h2>{{ this.$store.state.message }}</h2>
    <h3>all todo </h3>
    <li v-for="todo in $store.state.todos" v-bind:key="todo.id">
        {{ todo }}
    </li>
    <h3>done todos is</h3>
    <li v-for="doneTodo in doneTodos" v-bind:key="doneTodo.id">
        {{ doneTodo }}
    </li>
    <p>count is {{ doneTodosCount }}</p>
    <p>id=2 todo is {{ getTodoById }}</p>
</template>

<script>
import { mapGetters } from "vuex";

export default ({
    name: 'sampleContents',
    computed: {
        // mapGettersを使って読み込むことや
        ...mapGetters({
            doneTodos: 'doneTodos',
            doneTodosCount: 'doneTodosCount',
        }),
        // 下記のような方法で読み込むことも可能
        getTodoById () {
            return this.$store.getters.getTodoById(2);
        }
    },
})
</script>
```
![スクリーンショット 2022-07-24 17.21.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326068/d9dd46e8-ccda-9275-af6c-d0f4c5c7a1ac.png)

## `mutation`を使ってみる
`index.js`がどんどん大きくなってくるので、該当部分だけ抜き出します。
mutationはstateの状態を変えることができます。例えば、新しくToDoリストを追加することができます。
```vuex-demo/store/index.js
...
    mutations: {
        addTodo (state, todo) {
            state.todos.push( todo );
        }
    }
...
```
mutationが実行されるためには`commit`がトリガーになります。
つまり、`commit`がないことにはstateの状態を変えることができません。
```vuex-demo/src/components/contents.vue
<template>
    ...
    <form @submit.prevent="addTodo">
        <input
            type="text"
            v-model="text"
        />
        <button>Add ToDo</button>
    </form>
    ...
</template>

<script>
    ...
    methods: {
        addTodo () {
            // commitを使うことでstoreのtodoの状態を変えることができる
            this.$store.commit('addTodo', {
                "id": this.$store.state.todos.length + 1,
                "text": this.text,
                "done": false
            });
            this.text = '';
        },
    ...
</script>
```
![スクリーンショット 2022-07-24 18.38.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326068/3ac80352-f14e-9cdc-d21c-0581e7ef2a1d.png)


## `action`を使ってみる

actionはmutationと似ていますが、以下のような特徴があります。

- 状態の変更するのではなく、mutationのcommitを行なっている
- mutationと違い、非同期処理を含むことができる

mutationと違って、条件によって特定の処理を入れることができます。今回はidを設定できるようにします。
```vuex-demo/store/index.js
    ...
    actions: {
        addTodoSpecifingId ({commit}, number) {
            commit('addTodo', {id: number, text: 'Learn Linux', done: false});
        }
    }
```

```vuex-demo/src/components/contents.vue
<template>
   ...
    <h2>Add Todo by Actions</h2>
    <form @submit.prevent="addDoneTodo">
        <input
            type="integer"
            v-model="specific_id"
        />
        <button>Add ToDo by actions</button>
    </form>
    ...
</template>

<script>
    ...
    methods: {
        ...
        addDoneTodo () {
            // actionはdispatchによって呼び出される
            this.$store.dispatch('addTodoSpecifingId', this.specific_id)
        },
    ...
</script>
```

# 最後に
書きながらまとめることでなんとなくVuexを理解できた気がする。
公式が言っていた「状態管理パターン」の意味はstate, getter, mutation, actionがわかれば完璧ですね。