LoginSignup
2
1

More than 5 years have passed since last update.

vuexへの送信で引数を分かりやすくする方法

Last updated at Posted at 2018-09-02

現状

vuexでmutationやactionを送るときは、定数を使うとこんな感じになると思います。

vuexでよくある例
<template lang="pug">
#app
  .counter
    button(@click="onDecrementClick") -
    span {{ $store.state.count }}
    button(@click="onIncrementClick") +
  div
    input(type="number", v-model="value")
    button(@click="onAddButtonClick") add
</template>

<script>
import * as mutationTypes from '@/store/mutationTypes';

export default {
  name: "App",
  data() {
    return {
      value: 0
    }
  },
  methods: {
    onIncrementClick() {
      this.$store.commit(mutationTypes.INCREMENT);
    },
    onDecrementClick() {
      this.$store.commit(mutationTypes.DECREMENT);
    },
    onAddButtonClick() {
      const value = parseInt(this.$data.value, 10);
      this.$store.commit(mutationTypes.ADD, { value });
    }
  }
};
</script>

<style lang="scss" scoped>
.counter {
  span {
    padding: 0 10px;
  }
}
</style>

store側の設定はこんな感じになります。

storeの設定
import Vue from 'vue';
import Vuex from 'vuex';

import * as mutationTypes from './mutationTypes';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    // インクリメントする
    [mutationTypes.INCREMENT](state) {
      state.count += 1;
    },
    // デクリメントする
    [mutationTypes.DECREMENT](state) {
      state.count -= 1;
    },
    // 加算する
    [mutationTypes.ADD](state, { value }) {
      state.count += value;
    }
  }
});

mutation名のtypoを防ぐため定数にしており、そんなに悪くありませんが、一つだけ困ることがあります。
それは引数は何を渡すかを確認するのが面倒ということで、mutationTypes.ADDからコードジャンプしても定数の宣言部分に飛ぶだけで、肝心な引数の情報は載っていないので毎回store.jsで定義されているところを探しに行く羽目になります。
JS DOCも非常に書きづらくて、payloadの部分だけ書くというのはどうなんだろうって気になって、結局上のようにシングルラインコメントで操作することだけ書いています。

reduxの場合は、そもそもactionオブジェクトを作る関数があって、それを介して送っていました。
this.props.dispatch(createAction(10));
vuexは直接タイプ名をセットできるようにしてstoreへの送信を楽にさせたのだと思いますが、規模が大きくなると今回のようなやりづらさが出るんじゃないかなと思いました。
そこで冗長になってしまいますが、mutationオブジェクトを作る関数を用意することで、引数問題を解消しようというのが今回の記事です。

mutationオブジェクトの作成

基本的にvuexはタイプ名、payloadという順番で送ると思いますが、タイプ名を含めたオブジェクトで送ることができます。

// 通常の送り方
this.$store.commit('typeName', { value });

// オブジェクトでの送り方
this.$store.commit({
  type: 'typeName',
  value
});

このオブジェクト部分を関数で作るとこんな感じになります。

オブジェクトを返す関数を介してcommitする
/**
 * mutationオブジェクトを作る
 * @param {number} value - mutationに送信する値
 */
function createMutation(value) {
  return {
    type: 'typeName',
    value
  };
}

// mutationオブジェクトを作って、送信する
this.$store.commit(createMutation(10));

関数になったことでJS DOCも書きやすくなり、何を渡してあげなければいけないかが分かりやすくなったと思います。その代わり冗長になってしまいましたが・・・。

actionオブジェクトの作成

actionも同様にオブジェクトを作る関数を作ればいいのですが、その度にactionType名を作るのも面倒かなと思いました。loggerにもデフォルトでは表示されず、actionTypeとマッチするものをフックするだけの役割しかないなら、今回のように関数を別に用意した場合は不要かなと思いました。そこでredux-thunkのように非同期処理をするアクションだけを用意して、それを使いまわすようにしました。

store.js
import Vue from 'vue';
import Vuex from 'vuex';

import * as mutationTypes from './mutationTypes';
import * as actionTypes from './actionTypes';

Vue.use(Vuex);
export default new Vuex.Store({
  state: {
    data: 0
  },
  mutations: {
    // データをセットする
    [mutationTypes.SET_DATA](state, { data }) {
      state.data = data;
    }
  },
  actions: {
    // 関数を受け取り、commitを渡して実行するだけのアクション
    [actionTypes.THUNK]({ commit }, { method }) {
      return method(commit);
    }
  }
});
import { THUNK } from './actionTypes';
import { setData } from './mutationCreators';

/**
 * thunkアクションを生成する
 * @param {function} method - commitを引数にした関数
 */
function createThunkAction(method) {
  return {
    type: THUNK,
    method
  };
}

/**
 * データを取得する(テストなので通信はしない)
 */
export function fetchData() {
  return createThunkAction((commit) => {
    return new Promise((resolve) => {
      commit(setData(0));
      window.setTimeout(() => {
        commit(setData(Math.random()));
        resolve();
      }, 500);
    });
  });
}

サンプルコード

こちらにサンプルコードを置きましたので興味があるかたは見てください。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1