Vuexで状態管理しながらメモアプリを作ってみた

# メモアプリを Vuex で管理する
index.vue ファイルとコンポーネントだけで作れる簡単なアプリですが、 Vuex による状態管理まで理解したかったので今回 Vuex を取り入れることにしました。
小さいアプリケーションだとデータの流れが分かりやすいので、これから Vuex を学びたい人におすすめです。


## 前提条件

- Nuxt.js をインストール済　バージョン 6.4.1 （`npm -v`で確認)

- [ Nuxt.js 公式ドキュメント](https://ja.nuxtjs.org/guide/installation/)のチュートリアルを参考に、新しいプロジェクト名で 「create-nuxt-app」 の設定をします。作業はターミナル上で、ルートで行います（新しいプロジェクト名のディレクトリがルートに出来上がります）。これで簡単に開発の準備が整います。

- 何もない状態（スクラッチ）から始める場合も上記ドキュメントの中に、続けて記載がありますのでそちら参考にしてみてください。

ディレクトリができ、エディタを開くと↓写真のようなフォルダ一覧があります。[http://llocalhost:3000](http://localhost:3000)で確認すると、 pages フォルダの中の index.vue の内容になっています。 h1 タグのプロジェクト名の下に自動で description の文字が入っている場合があるので、変更するときは package.json を編集します。（写真の下から3番目）
<img width="210" alt="スクリーンショット 2019-07-30 13.00.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296366/c149815f-2dbe-8199-af44-59937457ac36.png">

## Flux の考え方 → Vuex を利用する場面
Flux についての説明は省きますが、 Vuex とは、[ドキュメント](https://vuex.vuejs.org/ja/)によると
> Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。

Vuex は一箇所にアプリケーションの状態（ステート）がまとまっています。つまり、"Single source of truth"を保てます。情報源を信頼できるものにしようという事で、下記のように流れは一方向です。
State を変更できるのは Mutations のみであり、 Actions は「コミット」しかできません。

<img width="638" alt="スクリーンショット 2019-07-30 16.23.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296366/3d812dcf-2463-080d-dce7-17dc53c60bf9.png">
<img width="640" alt="スクリーンショット 2019-07-30 16.25.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296366/4f202c52-5c10-89ef-0d25-1fcdb4925c5a.png">

## 必要なファイルを揃える
さっそく作っていきますが、必要なファイルを3つ用意します。ファイルは template タグでくるみます。私のエディタは Emmet が入っており`scaffold`と入力するとテンプレートが用意されます。

- pages/index.vue (最初に既にあるはずです)
- components/MemoList.vue (文字はパスカルケースで作成/ 再利用可能な部品のようなもので Model 。)
- store/memo.js ( state, mutations, actionsを今後記述します)

## フォームを作る
input タグを作るときは、`v-model`で Model, View の双方向バインディングを可能にします。片方変更したら、（ input の中身について DOM やデータに変更があれば）もう片方も同じように変更されます。
`@click`とは`v-on`のイベントハンドラの省略。
props でコンポーネントに値を渡す際は、ケバブケース`memo-contents`で渡します。コンポーネント側で受け取る時の属性はキャメルケース。


```index.vue
<div class="input_container">
  <p>
    <input
      v-model="inputText" 
      type="text"
     >
  </p>
  <button @click="addMemo()">Save</button>
</div>

<!-- コンポーネントに分けた部分について記述-->
<!-- memo-contents が属性で、" memoContents "はここでは変数です-->
<!-- @delete は、ただのきっかけで memo.js アクションの deleteMemo メソッドに伝えているだけ-->
<memo-list
  :memo-contents="memoContents"
  @delete="deleteMemo()"
/>
```

## コンポーネントを作る
input 入力したものは一つ一つがメモリストです。何度も使うのでコンポーネントとして MemoList.vue ファイルに記述します。

ここで`ul`タグの中で`v-for`を用いてリストを複数生成しています。
先に`script`タグの`props:`を見てください。`memoContents`があり、ここで配列を作っています。
先程のリストの複数生成時にはここのことを指しており、 memoContents に入っている分だけインデックスを付けてアイテム（ リスト ）を生成します。

```Memolist.vue
<template>
  <div>
    <ul
      v-for="(item, index) in memoContents"
      :key="index"
    >
      <li>
        <span>{{ item.text }}</span> <!-- ここについては後ほど -->
        <span>{{ item.date | dateConvert}}</span> <!-- ここについては後ほど -->
        <button @click="handleDelete(index)">x</button>
      </li>
    </ul>
  </div>
</template>

<script>
  export default {  //DOMに描き出すためにこのように書きます
    props: {    //親である index.vue から受け取るときはこの中に入ります
      memoContents: {  // memoContents として受け取り、値を一つずつ配列[]に入れます
        type: Array,
        default: () => []
      }
    },
```

## index.vue に追記
コンポーネントを組み込む際には import する必要があります。
まだ Vuex に関する memo.js は記述していませんが、ここで呼び出します。`methods`の中`addMemo()`については連想配列の階層がコンポーネントで書いたものと一致する必要があります。
`computed`では後述の memo.js の state の memoContents を参照。若干ややこしいですが、後述の memo.js と照らし合わせてみてください。

```index.vue
<script>
  // MemoList.vue を import したら、 exportdefault の中に compornent として記述
  import MemoList from '~/components/MemoList'
  import { mapState, mapActions } from 'vuex' // このように記述する

  export default {
    components: {  //ここでも描き出すためにMemoListを書きます
      MemoList
    },
    data: () => ({
      inputText: ""
    }),
    // 算出プロパティといいますが、 DOM へ表示されるものが computed です
    computed: {
      ...mapState({
        // どう index.vue で呼ぶか宣言して、 state の中の memo.js の中の memoContents を参照
        memoContents: state => state.memo.memoContents
      }),
      // ...mapState('memo', ['memoContents'])とも書けます
    },
    methods: {
      ...mapActions('memo', ['addMemoContents','deleteMemoContents']),
      addMemo() {
        // methods 内の関数として呼び出し
        this.addMemoContents({  //ここがコンポーネント側で{{ item.text }}で書いたところと階層が一致するはず
          text: this.inputText,
          date: new Date()
        })
        this.inputText = ""
      },
      // memoContens を消すものなので deleteMemo メソッドは index.vue にあるべき
      // 子( MemoList.vue からの)の index を受け取っている
      deleteMemo(index) {
        this.deleteMemoContents(index)
      }
    }
  }
</script>
```

## MemoList.vue に追記
ここで重要なものがもうひとつ、子から親へ値を渡す`$emit`です。

```MemoList.vue
<script>
  export default {   
  // component 内で完結するもの
    filters: {
      dateConvert: function (value) {
        const date = new Date(value)
        return `${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`
      }
    },
  // 親のメソッドを発火させるためだけのもの
  methods: {
    //上記のクリックイベントの index を受け取って $emit で親に受け渡す
    handleDelete(index) {
      this.$emit('delete', index)
    }
  }
```

## memo.js に記述
ようやくここまで来ました。今回重要なポイントは、「 mutations 以外は直接 state とやり取りしない」ことです。mutations が唯一 state を更新できるという事を踏まえて見ていきます。

```memo.js

// state  memoContentsが入っている状態
export const state = () => ({
  memoContents: []
})

//mutations データを更新できる
export const mutations = {
  //引数に state と書く。 memo は actions から渡される
  updateMemoContents(state, memo) {
    state.memoContents.push(memo)
  },
  deleteMemoItem(state, index) {
    state.memoContents.splice(index,1)
  }
}

// actions ・・ mutations を発動する
export const actions = {
  // { commit }と書くことで mutations に渡せる
  // index.vue からの memoDate を受け取ってから mutations に commit
  addMemoContents({ commit }, memoData) {
    commit('updateMemoContents', memoData)
  },
  deleteMemoContents({ commit }, index) {
    commit('deleteMemoItem', index)
  }
}

```

## まとめ
今回初めて Vuex を勉強しましたが、まだまだ理解が足りない部分が多く、冗長になってしまいました。 Vuex を使うべきアプリではありませんが、小さいアプリで全体像が捉えられたのは良かったです。