Help us understand the problem. What is going on with this article?

Reactユーザ必見!! Vuexの概念を学ぶ

More than 1 year has passed since last update.

概要

  • 普段Reactを使っていて、Vueも学ばないといけない
  • Vueは分かるけどVuexはよく分からない

って方の助けに成ればと思います。

読んでもらいたいポイント

  • Vueに必要なツール 
  • VueとVuexによるデータの流れ 
  • ゲッター(Getter) *
  • ストアとコンポーネント間の通信 *
  • ストアにアクセスするコンポーネントを絞る *

Vueに必要なツール

まず、Chromeの拡張ツールで、vue-devtoolsを導入しましょう。

次に、VS Codeを使っている人は拡張機能で Vetur を入れましょう。

Vuexの4大項目

項目名 大まかな概要
state 状態のことです 
getters stateをコンポーネントに渡す役割を持っています
mutations   stateを変更する役割を持っています。
actions 非同期通信やlocalStorageやAPI, BackEndとの通信などをします。

VueとVuexによるデータの流れ

  1. componentからactionを発火。
  2. actionをcommitし、mutationを発火。
  3. mutationによってstateの状態を変更します。
  4. gettersによって、componentにstateの状態を渡す。

Vuex

Vueにおける状態管理(ReactでいうRedux)です。
内部的にPromiseを採用しています。

store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import users from './users';

Vue.use(Vuex); // Vuexを使うことを宣言します

// ストアを作成する
const store = new Vuex.Store({

  // モジュールを分割することによって管理しやすくします。
  modules: {
    users,
  }
});

export default store;

Vuex周りのこと

モジュール(module)

一つのストアオブジェクトにstate, getters, mutation, actionを記述していく場合、管理するのが大変になるため、ストアをモジュールに分割します。

モジュールのmutationやgettersの中で渡される第一引数は、モジュールのローカルステートです。

アクション(action)

  • ReactでいうActionです。
  • mutationに対してcommitすることによって、mutationを発火できます。
  • 非同期的な処理やAPI, BackEndとの通信はactionで行います。

actionは、commit(Reactでいうdispatch)という命令をmutationにします。

store/users/actions.js
import * as types from './mutationType';

export default {
  addUser({ commit }, value) {
    // commit(mutation, 値);
    commit(types.createUser, value);
  }
}

コンポーネントでactionを使う

Top.vue
<template>
  <div>
    <form
      @submit.prevent="addUser(form)"
      style="padding: 32px 20px"
    >
      ID: <input type="text" >
      名前: <input type="text" >
      年齢: <input type="text" >
      <input type="submit">
    </form>

    <table border="1">
      <tr>
        <th>ID</th>
        <th>Name</th>
        <th>Age</th>
      </tr>
            <!-- for文です -->
      <tr
       v-for="(user, index) in users"
       :key="index"
      >
        <td>{{ user.id }}</td>
        <td>{{ user.name }}</td>
        <td>{{ user.age }}</td>
      </tr>
    </table>
  </div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex';

export default {
  name: 'Top', // vue devtoolsで表示されるコンポーネント名です。
  // 使う値を定義します。
  data () {
    return {
      users: '',
      form: {
        id: '',
        name: '',
        age: ''
      }
    };
  },

   // 算出関数を定義します
  //  computedでは、returnを必ず返すようにしないといけません。
 //   Vueでよく使われる機能の1つです。
  computed: {
    // mapGettersは、computedで定義します。
   //  mapGetters("名前空間名", ["gettersで定義した関数名"]
    ...mapGetters('users', [
       "getUsers",          
       "getUser"            
    ])
  },

  // vueインスタンスが作成された時に、発火します。
  created () {
     this.users = this.getUsers; // 下の書き方でもかけますが、オススメできません。
  },

  // methodsはeventや処理をする関数を定義します。
  // Vueでよく使われる機能の1つです。
  methods: {
    // mapActions("名前空間名", ["actionsで定義した関数名"])
    ...mapActions("users", ["addUser"]) 
  }
}
</script>

ミューテーションタイプ (mutation-type)

ReactでいうActionTypeです。

store/users/mutationType.js
export const createUser = "createUser";

ミューテーション(mutation)

  • ReactでいうReducerの役割で、stateの状態を変更する唯一の方法です。Vuexでは原則としてmutation以外がstateの更新を行うことを禁止しています。
  • mutationでは、なるべくstateの値を変更するだけにします。また、同期的な処理のみになります。
  • コンポーネント側から直接mutationを発火させて、stateの状態を変更させるのではなく、actionを通じてmutationを発火させた方がいいと思います。
store/users/mutations.js
import * as types from 'mutationType';

export default {
   // stateは、参照渡しです。 
  //  元の値をコピーではなく、stateの値を直接変更します。
  [types.createUser] (state, payload) {
    state.users.push(payload);
  }
}

ゲッター(getter)

  • stateの状態を算出したい時や、コンポーネント側にstateの状態を渡したい時に使います。
  • Reactにはない概念だと思います。
store/users/getters.js
export default {
 getUsers: state => {
    return state.users;
 },
 getUser: state => id => {
   const user = state.users.filter(user => {
      return user.id === id
   });
   return user;
 }
}

gettersをコンポーネント側で扱う

Top.vue
<template>
  <div>
    <table border="1">
      <tr>
        <th>ID</th>
        <th>Name</th>
        <th>Age</th>
      </tr>
            <!-- for文です -->
      <tr
       v-for="(user, index) in users"
       :key="index"
      >
        <td>{{ user.id }}</td>
        <td>{{ user.name }}</td>
        <td>{{ user.age }}</td>
      </tr>
    </table>
  </div>
</template>

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

export default {
  name: 'Top', // vue devtoolsで表示されるコンポーネント名です。
  // 使う値を定義します。
  data () {
    return {
      users: []
    };
  },

   // 算出関数を定義します
  //  computedでは、returnを必ず返すようにしないといけません。
 //   Vueでよく使われる機能の1つです。
  computed: {
      // mapGettersは、computedで定義します。
     //  mapGetters("名前空間名", ["gettersで定義した関数名"]
     ...mapGetters('users',
       "getUsers",          
       "getUser"             
    ])
  },
  // vueインスタンスが作成された時に、発火します。
  created () {
     this.users = this.getUsers; 
  }
}
</script>

ステート(state)

一部のコンポーネントでしか使用しないデータは、コンポーネントのdataオプションで管理し、アプリケーション全体で使用されるデータはストア内で管理するようにします。

store/users/index.js
import getters from './getters';
import mutations from './mutations';
import actions from './actions';

export default {
   // 名前空間を分けます。modulesで定義した名前空間で分けられます。
  //  この場合だと、"user"という名前空間で分けられます。
 //   デフォルトでは、全て同一の名前空間に登録されます。   
  namespaced: true, 

  // 状態を定義します。
  state: {
    users: [
      { id: 1, name: "hoge", age: 15},
      { id: 2, name: "あああ", age: 23 },
    ]
  },
  getters,
  mutations,
  actions
}

ディレクトリ構成

store [Directory]
 - index.js
 - mutationType.js
 users [Directory]
   - index.js
   - mutations.js
   - getters.js
   - actions.js

ストアとコンポーネント間の通信

  • this.$storeによるアクセス

this.$storeにはルートのコンポーネントのstoreオプションに渡されたストアのインスタンスが入っており、ステートの取得や、アクション、ミューテーションの実行などを 直接 行うことができます。
何度もthis.$storeと書いたり、直接変更などを行えるためあまりオススメできません。

  • ヘルパー関数によるアクセス

コンポーネントからストアを使うために用意されているヘルパー関数として、mapState, mapGetters, mapMutations, mapActionsがあります。

これらのヘルパー関数で、ステート、ゲッター、ミューテーション、アクションをコンポーネントの算出プロパティ・メソッドと結びつけられます。

こちらはストアのステート、ゲッター、ミューテーション、アクションをコンポーネント側に流し込むイメージです。

ストアにアクセスするコンポーネントを絞る

ReactのようにContainer Component(ロジックなどの処理があるコンポーネント)とPresentational Component(表示コンポーネント)に分けて、Container Componentからpropsを通してPresentational Componentに値を渡すようにすることで、変更に強くなります。

まとめ

この記事を通してVueやVuexについて、理解してくれた方や興味を持ってくれた方がいたら嬉しいです。
記事を書いてみて、うまく表現できない部分や説明が難しい部分があってVueについて、もっと勉強しないといけないなと思いました。

参考にさせて頂いたサイト

https://medium.com/studist-dev/ddd-vuex-c47055f6c1ba

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away