[Rails+Vue.js]に係るCRUD入門〜Part５: Vuex設定編〜

Rails+Vue.js+Webpackerによる「Create, Read, Update, Destroy」処理のチュートリアルを記述する。
なお，前提知識として，Railsチュートリアル終了程度を想定する。
# ＜概要＞
## ■ 本記事の内容
- Part4迄で作成した[BookHome.vue]のデータ処理について，Vuexの管理下に移行する。
- 今回のコードは，[GitHubのコミット履歴](https://github.com/masayuki-0319/rails-vue-bookshelf/commits/05_Vuex)で確認可能である。
    - [Vuexの導入]から[ヘルパー関数による省略技法]まで
- 本記事の参考URL
    - [Vuex公式](https://vuex.vuejs.org/ja/)
    - [Vue.js + Vuexでデータが循環する全体像を図解してみた](https://qiita.com/m_mitsuhide/items/f16d988ec491b7800ace)

## ■ 記事共通
- 目次
    - [ソースコード(GitHubへリンク)](https://github.com/masayuki-0319/rails-vue-bookshelf)
    - [〜Part１: 環境構築編](https://qiita.com/Moo_Moo_Farm/items/afacfe4349af6a106253)
    - [〜Part２: Read編](https://qiita.com/Moo_Moo_Farm/items/fdf133d4c2bcf6d207b6)
    - [〜Part３: Vue Router設定編](https://qiita.com/Moo_Moo_Farm/items/075e970287cb1aaf52c1)
    - [〜Part４: Create, Update, Delete編](https://qiita.com/Moo_Moo_Farm/items/8cf46e228d59417550b1)
    - [▲ Part５: Vuex設定編](https://qiita.com/Moo_Moo_Farm/items/fbc2e8c8f971dc5fe4bb)：本記事
    - [〜Part６: ユーザー登録&ログイン編](https://qiita.com/Moo_Moo_Farm/items/65aaa2ac7f8cef50fca6)
    - [〜Part Last: Docker化編](https://qiita.com/Moo_Moo_Farm/items/5975cd9d2d48ee35d6d6)

- 実装機能
    - お気に入りの本に係る登録，参照，編集，削除処理
    - 非同期通信（Ajax）による[Rails+Vue.js]のCRUD処理
    - SinglePageApplication（SPA）
    - ユーザー登録機能（JWTによるトークン認証）

- 開発環境
    - MacOS Mojave
    - Ruby(2.5.1)
    - Ruby on Rails(5.2.1)
    - Vue.js(2.6.10)
    - Yarn(1.17.0)
    - Webpack(4.39.2)

- 学習情報URL
    - [Vue.js公式](https://jp.vuejs.org/)
    - [Vue Mastery(The Vue Instance)](https://www.vuemastery.com/)

# ＜本文＞
## ■ Vuexの導入

**○１：Vuexをインストール**

```.bash
$ yarn add vuex
```

**○２：[store.js]を作成**

```app/javascript/store/store.js
import Vue from 'vue'
import Vuex from 'vuex'
import router from '../router/router.js'
import axios from 'axios'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {},
  mutations: {}
})
```

**○３：[application.js]に[store.js]を登録**

```app/javascript/packs/application.js
import Vue    from 'vue'
import App    from './App.vue'
import Router from '../router/router.js'
import Store  from '../store/store.js'

const app = new Vue({
   el: '#app',
   router: Router,
   store: Store,
   render: h => h(App)
})
```

## ■ [fetchBooks]関連を移行
**○１：[store.js]を修正**

```app/javascript/store/store.js
import Vue from 'vue'
import Vuex from 'vuex'
import router from '../router/router.js'
import axios from 'axios'

Vue.use(Vuex)

export default new Vuex.Store({
  // [state]がコンポーネントにおける[data]に相当
  state: {
    books: []
  },
  // [mutations]がコンポーネントにおける[methods]に相当
  mutations: {
    fetchBooks(state) {
      state.books = [];
      axios.get('/api/books').then((res) => {
        for(var i = 0; i < res.data.books.length; i++) {
          state.books.push(res.data.books[i]);
        }
      }, (error) => {
        console.log(error);
      });
    }
  },
})
```

**○２：[HomeBook.vue]を修正**

```app/javascript/pages/BookHome.vue
...
<script>
...
    data: function() {
      return {
        bookInfo: {},
        bookInfoBool: false,
        // [store.js]に移行するため，[books]を削除
      }
    },
    computed: {
       // [store.js]から[books]を呼び出して，[BookHome.vue]のdata[books]に格納
      books() {
       return this.$store.state.books
      }
    },
    mounted: function() {
      // [fetchBooks]を[store.js]から呼び出すため，コード修正
      this.$store.commit('fetchBooks')
    },
    methods: {
      // [store.js]に移行するため，[fetchBooks]を削除
      ...
    }
...
```
## ■ [setBookInfo]関連を移行

**○１：[store.js]を修正**

```app/javascript/store/store.js
...
  state: {
    books: [],
    bookInfo: {},
    bookInfoBool: false
  },
  mutations: {
    ...
    setBookInfo(state, { id } ) {
      axios.get(`api/books/${id}.json`).then(res => {
        state.bookInfo = res.data;
        state.bookInfoBool = true;
      });
    }
  }
})
```


**○２：[HomeBook.vue]を修正**

```app/javascript/pages/BookHome.vue
<script>
...
    // [data:]は，全て削除
    computed: {
     books() {
       return this.$store.state.books
     },
     // [bookInfo, bookInfoBool]を追加
     bookInfo() {
       return this.$store.state.bookInfo
     },
     bookInfoBool() {
       return this.$store.state.bookInfoBool
     }
    },
    ...
    methods: {
    // [setBookInfo]を[store.js]から呼び出すため，コード修正
      setBookInfo(id) {
        this.$store.commit('setBookInfo', { id })
      },
...
```

## ■ [deleteBook]関連を移行

**○１：[store.js]を修正**

```app/javascript/store/store.js
...
  mutations: {
    ...
    deleteBook(state, { id } ) {
      axios.delete(`/api/books/${id}`).then(res => {
        state.bookInfo = '';
        state.bookInfoBool = false;
      });
    }
...
```
**○２：[HomeBook.vue]を修正**

```app/javascript/pages/BookHome.vue
<script>
...
    methods: {
      setBookInfo(id) {
        this.$store.commit('setBookInfo', { id })
      },
      // [deleteBook]のコードを修正
      deleteBook(id) {
        this.$store.commit('deleteBook', { id })
        this.$store.commit('fetchBooks')
      },
    }
...
```

## ■ ヘルパー関数による省略技法

- ストアのstateを呼び出す際のコードが冗長であるため，ヘルパー関数を使用して，コードを簡略化する。
- mapStateと同様に，mapMutations, mapGetters, mapActions等も存在するが，本記事では使用しない。
- 参考URL
    - [公式：コンポーネントをバインドするヘルパー](https://vuex.vuejs.org/ja/api/#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%82%92%E3%83%90%E3%82%A4%E3%83%B3%E3%83%89%E3%81%99%E3%82%8B%E3%83%98%E3%83%AB%E3%83%91%E3%83%BC)
    - [Vuex: mapStateの使い方を理解する](https://qiita.com/Statham/items/046649bff81c00ba275e)
    - [Vuex：mapStateの書き方8パターン+11サンプルコード](https://qiita.com/suin/items/7331905a45a8ff80d4dd)


```app/javascript/store/store.js
...
<script>
  import axios from 'axios'
  import { mapState } from 'vuex'

  export default {
    name: 'BookHome',
    computed: mapState([
      'books',
      'bookInfo',
      'bookInfoBool',
    ]),
...

/* 上記のコードと同等
 ~Ver.1：mapStateを使用して，異なるデータ名に格納したい場合~
    computed: mapState({
      books: 'books',
      bookInfo: 'bookInfo',
      bookInfoBool: 'bookInfoBool'
    })

 ~Ver.2：デフォルト~
    computed: {
     　books() {
    　   return this.$store.state.books
  　   },
     　bookInfo() {
   　    return this.$store.state.bookInfo
 　    },
    　 bookInfoBool() {
  　     return this.$store.state.bookInfoBool
　     }
    }
*/
```

## ■ 特記事項
**○１：[action，getter]について**
　今後チュートリアルで必要性が出てきたら追記します。

**○２：Vuexの状態管理下に置くデータについて**
　Vuexの機能上，全てのデータを管理下に置くことは可能であり，複雑でもModuleを使用したら実装できるかと思います。
　しかし，一つのコンポーネントでしか使用しないデータを共通の管理下に置くことは，冗長になり，管理が大変になることが予想されますので，以下の情報が判断の参考になるかと思い引用します。

>- ステートに適したデータ
>  - サーバーからデータを取得中かどうかを表すフラグ
>  - ログイン中のユーザー情報など，アプリケーション全体で使用されるデータ
>  - ECサイトにおける商品の情報など，アプリケーションの複数の場所で使用される可能性のあるデータ
>- コンポーネント側で持つべきデータ
>  - マウスポインタがある要素の上に存在するかどうかを表すフラグ
>  - ドラッグ中の要素の座標
>  - 入力中のフォームの値

> 【引用：Vue.js入門~基礎から実践アプリケーション開発まで~ 245-246.】



〜Part５: Vuex設定編終了〜
**[〜Part６: ユーザー登録&ログイン編へ進む〜](https://qiita.com/Moo_Moo_Farm/items/65aaa2ac7f8cef50fca6)**