【Vuex】stateの変更を監視する3つの方法
現在バージョンアップ作業している所得税・住民税・事業税・国民健康保険計算シミュレーションは、Vuexを使用しています。その際に、stateの変更を監視したいケースが出てきたのでその方法をご紹介します。
今回の想定は、都道府県のセレクトコントロールがあり、その値をVuexのストアで管理しているケースです。画面はこんな感じのコントロール。いたって普通。
Vuexのstoreの中身はこちら。
const store = new Vuex.Store({
state: {
prefecture: null
},
getters: {
prefecture(state) { return state.prefecture }
},
mutations: {
setPrefecture(state, payload) {
state.prefecture = payload.prefecture
}
},
actions: {
doUpdatePrefecture({ commit }, prefecture) {
commit('setPrefecture', { prefecture })
}
}
})
Vue.jsの場合
比較用でまずはVuexを使用していないデータや算出プロパティを変更監視する方法を。Vue.jsでは以下のようにwatchを使います。楽ちん。
new Vue(
data() {
return {
prefecture: '福岡県'
}
},
watch: {
prefecture: function(newValue, oldValue) {
console.log('prefecture changed! %s => %s', oldValue, newValue)
}
}
)
Vuexでストアの状態を監視する方法は3つ
ケースによって使い分けができるように、3種類あります。
- watch
- subscribe
- subscribeAction ※ Vuex 2.5.0~
subscribeとsubscribeActionは監視というよりは、イベントフックみたいなものでしょうか。
それでは早速それぞれのケースを見ていきます。
※ VuexがVueのルートコンポーネントにstore
オプションと指定されていることとします。
watch
Vue.jsのwatch(ウォッチャ)と考え方は同じです。stateの変更を直接監視します。
export default {
…
mounted() {
this.$store.watch(
(state, getters) => getters.prefecture,
(newValue, oldValue) => {
console.log('prefecture changed! %s => %s', oldValue, newValue)
}
)
}
}
結果はこちら
prefecture changed! null => 福岡県
prefecture changed! 福岡県 => 東京都
変更前と変更後の値が、Vue.jsのwatchと同じように取得できてます。
subscribe
Vuexのドキュメントには、
ストアへのミューテーションを購読します。handler は、全てのミューテーションの後に呼ばれ、引数として、ミューテーション ディスクリプタとミューテーション後の状態を受け取ります。
とあります。ミューテーションが実行された後なので、値が変わった後に処理を挟むことができるということですね。
購読するミューテーションは全てとなるので、どのミューテーションが呼ばれたかはmutation.type
で判定します。
export default {
…
mounted() {
this.$store.subscribe((mutation, state) => {
if (mutation.type === 'setPrefecture') {
console.log('update prefecture! %s', state.prefecture);
}
)
}
}
結果
update prefecture! 福岡県
update prefecture! 東京都
変更前の値が必要ない場合はこちらでもOKですね。
subscribeAction
subscribeはミューテーションの購読ですが、こちらはアクションに対しての購読みたいですね。Vuexのドキュメントには、ストアアクションを購読します。handler はディスパッチされたアクションごとに呼び出され、アクション記述子と現在のストア状態を引数として受け取ります
とだけ書かれてます。これもアクション実行後なのかと思いましたが違いました。アクション実行前でした。
ちなみに、Vuex2.5.0で新規追加された機能なのでご注意を。
引数のactionにはアクションコール時の情報が、stateには現在の状態なので変更前と変更後の値が参照可能です。
export default {
…
mounted() {
this.$store.subscribeAction((action, state) => {
if (action.type === 'doUpdatePrefecture') {
console.log('Call doUpdatePrefecture action! %s', state.prefecture);
}
)
}
}
結果
Call doUpdatePrefecture action! null => 福岡県
Call doUpdatePrefecture action! 福岡県 => 東京都
Vuex 3.1.0以降
3.1.0からは、アクション実行前と実行後のどちらかを指定できるようになり、さらにコントロールできるようになりました。
デフォルトの動作は’before’ということなので、アクション実行前です。
export default {
…
mounted() {
this.$store.subscribeAction({
before: (action, state) => {
if (action.type === 'doUpdatePrefecture') {
console.log('prefecture changed by action before! state: %s action.payload: ', state.InputItem.prefecture, action.payload)
}
},
after: (action, state) => {
if (action.type === 'doUpdatePrefecture') {
console.log('prefecture changed by action after! state: %s action.payload: ', state.InputItem.prefecture, action.payload)
}
}
}
}
結果
prefecture changed by action before! state: null action.payload: 福岡県
prefecture changed by action after! state: 福岡県 action.payload: 福岡県
prefecture changed by action before! state: 福岡県 action.payload: 東京都
prefecture changed by action after! state: 東京都 action.payload: 東京都
アクション実行前と実行後で、stateの中身が更新されているのがわかります。
watch、subscribe、subscribeActionの処理順
3パターン全てを指定した場合の実行順。
- subscribeAction.before
- subscribe
- subscribeAction.after
- watch
結果
// 1
prefecture changed by action before! state: null action.payload: 福岡県
// 2
update prefecture! 福岡県
// 3
prefecture changed by action after! state: 福岡県 action.payload: 福岡県
// 4
prefecture changed! null => 福岡県
watchとsubscribe(subscribeAction)の使い分け
-
stateを
直接
監視したい場合は`watch’を使う -
ミューテーションやアクションに処理を挟みたい場合はsubscribe(subscribeAction)を使う
当たり前ですが、他のミューテーションで監視しようとしている値を変更した場合、この変更を感知できるのは`watchだけ’です。
const store = new Vuex.Store({
state: {
prefecture: null,
hoge: null
},
getters: …
mutations: {
setPrefecture(state, payload) {
state.prefecture = payload.prefecture
},
setHoge(state, payload) {
state.hoge = payload.hoge
if (hoge == 'fuga') {
// ここの変更を監視できるのはwatchのみ
state.prefecture = '福岡県'
}
}
},
actions: …
})
まとめ
Vuexの初期化時やlocalStorageから値を取得しストアにセットする場合はスルーして、ユーザー入力時にのみ動かしたいとか色々なケースがあると思います。各性質を正しく理解し、監視したい値に対してのような処理を挟みたいのかを考え、3パターンを使い分けるとよさそうですね。
ディスカッション
コメント一覧
まだ、コメントがありません