vue.js 2 vuexからストア値を監視する方法


168

一緒に使っvuexvuejs 2います。

私はに不慣れです。変数の変化vuexを見たいのstoreですが。

watch関数を追加したいvue component

これは私がこれまでに持っているものです:

import Vue from 'vue';
import {
  MY_STATE,
} from './../../mutation-types';

export default {
  [MY_STATE](state, token) {
    state.my_state = token;
  },
};

に変更があるかどうか知りたい my_state

store.my_statevuejsコンポーネントでどのように監視しますか?


便利な
Chromeに

回答:


206

レッツあなたが果物のバスケットを持っている、とあなたはバスケットから果物を追加または削除するたびに、あなたは(1)にしたいことを例えばたとえば、フルーツ数についての表示情報、しかし、あなたはまた、(2)を通知することにしたいですおしゃれな果物の数...

fruit-count-component.vue

<template>
  <!-- We meet our first objective (1) by simply -->
  <!-- binding to the count property. -->
  <p>Fruits: {{ count }}</p>
</template>

<script>
import basket from '../resources/fruit-basket'

export default () {
  computed: {
    count () {
      return basket.state.fruits.length
      // Or return basket.getters.fruitsCount
      // (depends on your design decisions).
    }
  },
  watch: {
    count (newCount, oldCount) {
      // Our fancy notification (2).
      console.log(`We have ${newCount} fruits now, yay!`)
    }
  }
}
</script>

watchオブジェクト内の関数の名前は、computedオブジェクト内の関数の名前と一致する必要があることに注意してください。上記の例では、名前はcountです。

監視されるプロパティの新しい値と古い値は、パラメーターとして監視コールバック(カウント関数)に渡されます。

バスケットストアは次のようになります。

fruit-basket.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const basket = new Vuex.Store({
  state: {
    fruits: []
  },
  getters: {
    fruitsCount (state) {
      return state.fruits.length
    }
  }
  // Obviously you would need some mutations and actions,
  // but to make example cleaner I'll skip this part.
})

export default basket

詳しくは、次のリソースをご覧ください。


私はちょうどその時私はshoul何疑問に思ってwatchアクションは2つのステップに分けなければならない:1)まず、チェック欲求データがキャッシュされている場合はそれだけでキャッシュされたデータを返さない場合。2)キャッシュが失敗した場合、データを取得するための非同期のajaxアクションが必要ですが、これはactionの作業のようです。私の質問が意味をなすことを願って、ありがとう!
1Cr18Ni9

コンポーネントのウォッチャーを設定するだけのmicah5の答えよりも、ストアの値にこれの利点は何ですか?保守するコードが少なくなります。
エキソセントリック

@Exocentric私が答えを書いたとき、質問がはっきりしませんでした。プロパティを監視する必要がある理由はありません。「変数Xを監視したいので、Yを実行できる」と考えてください。おそらくそれが、ほとんどの回答が非常に異なるアプローチを提案する理由です。意図が何であるか誰も知りません。それが私が私の答えに「目的」を含めた理由です。別の目的がある場合は、別の答えが当てはまる場合があります。私の例は、実験の開始点にすぎません。プラグアンドプレイソリューションを意図したものではありません。メリットは状況によって異なるため、「メリット」はありません。
Anastazy

@ 1Cr18Ni9キャッシュはコンポーネントコードに属さないと思います。本当にシンプルなもの(データをフェッチし、それをビューにバインドする)を過剰設計することになります。キャッシュはブラウザにすでに実装されています。サーバーから正しいヘッダーを送信することで、これを利用できます。ここで簡単な説明:csswizardry.com/2019/03/cache-control-for-civilians。また、インターネットに接続していなくてもWebサイトが機能するServiceWorkersを確認することもできます。
Anastazy

61

コンポーネントのウォッチャーを使用して状態の変化をリッスンしないでください。ゲッター関数を使用してコンポーネント内にマッピングすることをお勧めします。

import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters({
      myState: 'getMyState'
    })
  }
}

あなたの店で:

const getters = {
  getMyState: state => state.my_state
}

this.myStateコンポーネントでを使用してストアに加えられた変更をリッスンできるはずです。

https://vuex.vuejs.org/en/getters.html#the-mapgetters-helper


1
mapGettersの実装方法がわかりません。例を挙げてください。それは大きな助けになるでしょう。現時点では、GONG Answerを実装しています。TY
Rbex 2017年

1
@Rbex "mapGetters"は 'vuex'ライブラリの一部です。実装する必要はありません。
ガブリエルロバート

69
この答えは間違っています。彼は実際に計算されたプロパティを監視する必要があります。
フアン

15
一度呼び出されたゲッターは、そのときの状態のみを取得します。そのプロパティに別のコンポーネントからの状態変化を反映させたい場合は、それを監視する必要があります。
Cティアニー

3
なぜ「コンポーネントのウォッチャーを使用して状態変化をリッスンすべきではない」のですか?状態からトークンを監視したい場合、および別のページにリダイレクトするように変更された場合について、あなたが考えないかもしれない例を以下に示します。そのため、それを行う必要がある場合があります。それを知るには、もっと経験が必要かもしれません。
Shlomi Levi

46

それは次のように簡単です:

watch: {
  '$store.state.drawer': function() {
    console.log(this.$store.state.drawer)
  }
}

6
これは、ここでのどの回答よりも単純明快な光景です。
Inigo

19
単純すぎるのでjsのようには見えません。jsはもっと複雑でなければなりません。
digout

1
もしそうならfunction(n) { console.log(n); }
もっと単純

2
超クール。このアプローチの欠点にも関心があります。これまでのところ、うまく機能しているようです。
namero999

1
真剣に。これは、監視と計算で重複する関数名を必要とする、受け入れられた回答よりもはるかに優れているようです。専門家は、なぜこのようにするのか、なぜそうしないのかについてコメントできますか?
エキソセントリック

42

上記のように、店舗で直接変更を監視することはお勧めしません

しかし、いくつかの非常にまれなケースでは、それが誰かにとって役立つかもしれないので、私はこの答えを残します。その他のケースについては、@ gabriel-robertの回答を参照してください

これはから行うことができますstate.$watchcreatedこれをコンポーネントのあなたの(またはこれを実行する必要がある)メソッドに追加します

this.$store.watch(
    function (state) {
        return state.my_state;
    },
    function () {
        //do something on data change
    },
    {
        deep: true //add this if u need to watch object properties change etc.
    }
);

詳細:https : //vuex.vuejs.org/api/#watch


3
直接状態を見るのは良い考えではないと思います。ゲッターを使うべきです。vuex.vuejs.org/en/getters.html#the-mapgetters-helper
Gabriel Robert

14
@GabrielRobert私は両方のための場所があると思います。テンプレートの条件に基づいて事後的に変更する必要がある場合は、mapStateなどで計算値を使用するのが理にかなっています。しかし、それ以外の場合は、コンポーネントのフロー制御の場合と同様に、完全な監視が必要です。
正解

14
誰もがそれを言及していますが、誰もその理由を述べていません!変更時にDBと自動同期されるvuexストアを構築しようとしています。店内のウォッチャーが最もスムーズな方法だと思います。どう思いますか?それでも良い考えではありませんか?
mesqueeb

16

質問者はVuexで時計を使用したいと考えています。

this.$store.watch(
      (state)=>{
        return this.$store.getters.your_getter
      },
      (val)=>{
       //something changed do something

      },
      {
        deep:true
      }
      );

13

これはゲッターとの問題を解決し、実際には本当に(非VUE第三者のものに話をするなど、ウォッチャーを必要とすることはできませんすべての人々のためにあるのVueウォッチャーをウォッチャーを使用するときに)。

Vueコンポーネントのウォッチャーと計算値はどちらも計算値で機能します。したがって、vuexでも同じです。

import { mapState } from 'vuex';

export default {
    computed: {
        ...mapState(['somestate']),
        someComputedLocalState() {
            // is triggered whenever the store state changes
            return this.somestate + ' works too';
        }
    },
    watch: {
        somestate(val, oldVal) {
            // is triggered whenever the store state changes
            console.log('do stuff', val, oldVal);
        }
    }
}

ローカルとグローバルの状態を組み合わせるだけの場合、 mapStateのドキュメントにも例が記載されています。

computed: {
    ...mapState({
        // to access local state with `this`, a normal function must be used
        countPlusLocalState (state) {
          return state.count + this.localCount
        }
    }
})

素敵なハックですが、面倒すぎますね。
Martian2049

2
それがドキュメントにあればハックではありませんか?しかし、それはvue / vuexの賛成論でもありません
dube

8

typescriptを使用すると、次のことができます。

import { Watch } from "vue-property-decorator";

..

@Watch("$store.state.something")
private watchSomething() {
   // use this.$store.state.something for access
   ...
}


なぜこれは正確に反対投票されたのですか?ソリューションがvue-class-componentであり、TOが古いvue-classスタイルを要求していたからといって?前者が望ましいと思います。おそらく@Zhang Solは、これが明示的にvue-class-componentのためのものであることを冒頭で言及できますか?
JackLeEmmerdeur

注意してくださいなぜtypescriptですデコレータはこのような単純なVUEネイティブ解決することが好ましい:stackoverflow.com/a/56461539/3652783
yann_yinn

6

値の変化を監視して設定することにより、ストア変数のローカル状態を作成します。フォーム入力vモデルのローカル変数の変更によって、ストア変数が直接変更されることはありません。

data() {
  return {
    localState: null
  };
 },
 computed: {
  ...mapGetters({
    computedGlobalStateVariable: 'state/globalStateVariable'
  })
 },
 watch: {
  computedGlobalStateVariable: 'setLocalState'
 },
 methods: {
  setLocalState(value) {
   this.localState = Object.assign({}, value);
  }
 }

5

店舗の変更を監視する最良の方法はmapGetters、ガブリエルが言ったように使用することです。しかし、それを行うことができない場合があります。mapGettersたとえば、パラメーターを使用してストアから何かを取得したい場合があります。

getters: {
  getTodoById: (state, getters) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

その場合は使用できませんmapGetters。代わりに次のようなことを試してみてください:

computed: {
    todoById() {
        return this.$store.getters.getTodoById(this.id)
    }
}

しかし、残念ながら変更されたtodoById 場合にのみ更新さthis.idれます

このような場合にコンポーネントを更新する場合this.$store.watch は、Gongが提供するソリューションを使用してください。または、意識的にコンポーネントを処理し、更新this.idが必要なときに更新しtodoByIdます。


ありがとうございました。それはまさに私のユースケースであり、実際にはそのときゲッターは監視できません...
pscheit

5

単に状態プロパティ監視し、そのプロパティの変更に応じてコンポーネント内で動作したい場合は、以下の例を参照してください。

store.js

export const state = () => ({
 isClosed: false
})
export const mutations = {
 closeWindow(state, payload) {
  state.isClosed = payload
 }
}

このシナリオでは、次のbooleanようにアプリケーションのさまざまな場所で変更する状態プロパティを作成しています。

this.$store.commit('closeWindow', true)

ここで、他のコンポーネントでその状態プロパティを監視し、ローカルプロパティを変更する必要がある場合は、mountedフックに次のように記述します。

mounted() {
 this.$store.watch(
  state => state.isClosed,
  (value) => {
   if (value) { this.localProperty = 'edit' }
  }
 )
}

まず、stateプロパティにウォッチャーを設定し、コールバック関数valueでそのプロパティのを使用してを変更しlocalPropertyます。

お役に立てば幸いです。


3

状態レベルで監視したい場合は、次の方法で行うことができます。

let App = new Vue({
    //...
    store,
    watch: {
        '$store.state.myState': function (newVal) {
            console.log(newVal);
            store.dispatch('handleMyStateChange');
        }
    },
    //...
});

この動作はコンポーネントを使用する場合にのみ機能するため、コンポーネントからの状態アクションstore.stateによる変更を処理することはお勧めできませんdispatch。また、無限ループで終了する場合もあります。store.state変更に注意して使用することはめったにありません。たとえば、vsstore.statenewValueoldValue
Januartha

@Januarthaこの問題に対するあなたの提案は何ですか?
Billal Begueradj 2018

@Andyはい、もちろんその仕事です。なぜあなたが電話するのstore.dispatchか注意したいだけですか?store.mutations`のstore.state変更を処理する場合はstore' why not handle it inside
Januartha 2018

@BillalBEGUERADJ私はprever dubeソリューションがよりクリーンです
Januartha

@Januartha、変異を行う前にajax呼び出しが発生する可能性があるため、store.dispatch最初に使用するのはそのためです。たとえば、$store.state.country変更があるたびに国からすべての都市を取得したいので、これをウォッチャーに追加します。それから私はajax呼び出しを書きます:store.dispatch('fetchCities')私は書きます:axios.get('cities',{params:{country: state.country }}).then(response => store.commit('receiveCities',response) )
Andy

2

Vuex アクションゲッター計算されたプロパティウォッチャーの組み合わせを使用して、Vuex状態値の変更をリッスンできます。

HTMLコード:

<div id="app" :style='style'>
  <input v-model='computedColor' type="text" placeholder='Background Color'>
</div>

JavaScriptコード:

'use strict'

Vue.use(Vuex)

const { mapGetters, mapActions, Store } = Vuex

new Vue({
    el: '#app',
  store: new Store({
    state: {
      color: 'red'
    },
    getters: {
      color({color}) {
        return color
      }
    },
    mutations: {
      setColor(state, payload) {
        state.color = payload
      }
    },
    actions: {
      setColor({commit}, payload) {
        commit('setColor', payload)
      }
    }
  }),
  methods: {
    ...mapGetters([
        'color'
    ]),
    ...mapActions([
        'setColor'
    ])
  },
  computed: {
    computedColor: {
        set(value) {
        this.setColor(value)
      },
      get() {
        return this.color()
      }
    },
    style() {
        return `background-color: ${this.computedColor};`
    }
  },
  watch: {
    computedColor() {
        console.log(`Watcher in use @${new Date().getTime()}`)
    }
  }
})

JSFiddleデモを参照してください


1

ストアの変更をサブスクライブすることもできます。

store.subscribe((mutation, state) => {
  console.log(mutation.type)
  console.log(mutation.payload)
})

https://vuex.vuejs.org/api/#subscribe


これをコンポーネントのbeforeMount()フックで起動してから、ifステートメントで着信ミューテーションをフィルタリングできます。例:if(mutation.type == "names / SET_NAMES"){...何かを行う}
アレハンドロ

1

コンポーネント内で、計算された関数を作成します

computed:{
  myState:function(){
    return this.$store.state.my_state; // return the state value in `my_state`
  }
}

これで、計算された関数名を監視できます。

watch:{
  myState:function(newVal,oldVal){
    // this function will trigger when ever the value of `my_state` changes
  }
}

vuex状態に加えられた変更はmy_state、計算された関数に反映されmyState、監視機能をトリガーします。

状態my_stateがネストされたデータを持っている場合、handlerオプションはさらに役立ちます

watch:{
  myState:{
    handler:function(newVal,oldVal){
      // this function will trigger when ever the value of `my_state` changes
    },
    deep:true
  }
}

これにより、ストア内のすべてのネストされた値が監視されますmy_state


0

vueコンポーネントでmapStateを使用して、ストアから状態を取得するように指示することもできます。

あなたのコンポーネントで:

computed: mapState([
  'my_state'
])

my_stateストアの変数はどこにありますか。


0

====== store =====
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    showRegisterLoginPage: true,
    user: null,
    allitem: null,
    productShow: null,
    userCart: null
  },
  mutations: {
    SET_USERS(state, payload) {
      state.user = payload
    },
    HIDE_LOGIN(state) {
      state.showRegisterLoginPage = false
    },
    SHOW_LOGIN(state) {
      state.showRegisterLoginPage = true
    },
    SET_ALLITEM(state, payload) {
      state.allitem = payload
    },
    SET_PRODUCTSHOW(state, payload) {
      state.productShow = payload
    },
    SET_USERCART(state, payload) {
      state.userCart = payload
    }
  },
  actions: {
    getUserLogin({ commit }) {
      axios({
        method: 'get',
        url: 'http://localhost:3000/users',
        headers: {
          token: localStorage.getItem('token')
        }
      })
        .then(({ data }) => {
          // console.log(data)
          commit('SET_USERS', data)
        })
        .catch(err => {
          console.log(err)
        })
    },
    addItem({ dispatch }, payload) {
      let formData = new FormData()
      formData.append('name', payload.name)
      formData.append('file', payload.file)
      formData.append('category', payload.category)
      formData.append('price', payload.price)
      formData.append('stock', payload.stock)
      formData.append('description', payload.description)
      axios({
        method: 'post',
        url: 'http://localhost:3000/products',
        data: formData,
        headers: {
          token: localStorage.getItem('token')
        }
      })
        .then(({ data }) => {
          // console.log('data hasbeen created ', data)
          dispatch('getAllItem')
        })
        .catch(err => {
          console.log(err)
        })
    },
    getAllItem({ commit }) {
      axios({
        method: 'get',
        url: 'http://localhost:3000/products'
      })
        .then(({ data }) => {
          // console.log(data)
          commit('SET_ALLITEM', data)
        })
        .catch(err => {
          console.log(err)
        })
    },
    addUserCart({ dispatch }, { payload, productId }) {
      let newCart = {
        count: payload
      }
      // console.log('ini dari store nya', productId)

      axios({
        method: 'post',
        url: `http://localhost:3000/transactions/${productId}`,
        data: newCart,
        headers: {
          token: localStorage.getItem('token')
        }
      })
        .then(({ data }) => {
          dispatch('getUserCart')
          // console.log('cart hasbeen added ', data)
        })
        .catch(err => {
          console.log(err)
        })
    },
    getUserCart({ commit }) {
      axios({
        method: 'get',
        url: 'http://localhost:3000/transactions/user',
        headers: {
          token: localStorage.getItem('token')
        }
      })
        .then(({ data }) => {
          // console.log(data)
          commit('SET_USERCART', data)
        })
        .catch(err => {
          console.log(err)
        })
    },
    cartCheckout({ commit, dispatch }, transactionId) {
      let count = null
      axios({
        method: 'post',
        url: `http://localhost:3000/transactions/checkout/${transactionId}`,
        headers: {
          token: localStorage.getItem('token')
        },
        data: {
          sesuatu: 'sesuatu'
        }
      })
        .then(({ data }) => {
          count = data.count
          console.log(count, data)

          dispatch('getUserCart')
        })
        .catch(err => {
          console.log(err)
        })
    },
    deleteTransactions({ dispatch }, transactionId) {
      axios({
        method: 'delete',
        url: `http://localhost:3000/transactions/${transactionId}`,
        headers: {
          token: localStorage.getItem('token')
        }
      })
        .then(({ data }) => {
          console.log('success delete')

          dispatch('getUserCart')
        })
        .catch(err => {
          console.log(err)
        })
    }
  },
  modules: {}
})


1
サイトへようこそ。コードスニペットのみを配置するだけでは不十分です。コードについて説明してください。
pgk

0

私はこの方法を使用し、それは機能します:

store.js:

const state = {
  createSuccess: false
};

Mutation.js

[mutations.CREATE_SUCCESS](state, payload) {
    state.createSuccess = payload;
}

actions.js

async [mutations.STORE]({ commit }, payload) {
  try {
    let result = await axios.post('/api/admin/users', payload);
    commit(mutations.CREATE_SUCCESS, user);
  } catch (err) {
    console.log(err);
  }
}

getters.js

isSuccess: state => {
    return state.createSuccess
}

そして、ストアからの状態を使用するコンポーネントでは:

watch: {
    isSuccess(value) {
      if (value) {
        this.$notify({
          title: "Success",
          message: "Create user success",
          type: "success"
        });
      }
    }
  }

ユーザーがフォームを送信すると、アクションSTOREが呼び出され、作成が成功した後、CREATE_SUCCESSミューテーションがその後コミットされます。ターンcreateSuccessをがtrueであり、コンポーネントでウォッチャーは値が変更されたことを確認し、通知をトリガーします。

isSuccessgetters.jsで宣言した名前と一致する必要があります

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.