vue.js 2 comment regarder les valeurs de magasin depuis vuex

170

J'utilise vuexet vuejs 2ensemble.

Je suis nouveau vuex, je veux voir un storechangement variable.

Je veux ajouter la watchfonction dans monvue component

Voici ce que j'ai jusqu'à présent:

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

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

Je veux savoir s'il y a des changements dans le my_state

Comment regarder store.my_statedans mon composant vuejs?

raffffffff
la source
utilisez un plugin Vuejs qui est livré avec chrome, il vous sera utile
AKASH PANDEY

Réponses:

206

Disons, par exemple, que vous avez un panier de fruits, et chaque fois que vous ajoutez ou supprimez un fruit du panier, vous voulez (1) afficher des informations sur le nombre de fruits, mais vous voulez également (2) être informé de le compte des fruits d'une manière fantaisiste ...

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>

Veuillez noter que le nom de la fonction dans l' watchobjet doit correspondre au nom de la fonction dans l' computedobjet. Dans l'exemple ci-dessus, le nom est count.

Les nouvelles et anciennes valeurs d'une propriété surveillée seront transmises au rappel de surveillance (la fonction de comptage) en tant que paramètres.

Le magasin de paniers pourrait ressembler à ceci:

corbeille de fruits.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

Vous pouvez en savoir plus dans les ressources suivantes:

Anastazy
la source
Je me demande simplement ce que je dois faire lorsque l' watchaction doit se diviser en deux étapes: 1) Tout d'abord, vérifier si les données souhaitées sont mises en cache et si elles ne font que renvoyer les données mises en cache; 2) Si le cache échoue, j'ai besoin d'une action asynchrone ajax pour récupérer les données, mais cela semble être le actiontravail du. En espérant que ma question a du sens, merci!
1Cr18Ni9
Quel est l'avantage de cela, par rapport à la réponse de micah5, qui ne fait que définir un observateur dans le composant, sur la valeur de magasin? Il a moins de code à maintenir.
Exocentric
@Exocentric Quand j'ai écrit la réponse, la question n'était pas claire pour moi. Il n'y a pas de contexte pour lequel il est nécessaire de surveiller les propriétés. Pensez-y comme: "Je veux regarder la variable X, donc je peux faire Y." C'est probablement pourquoi la plupart des réponses proposent des approches si différentes. Personne ne sait quelle est l'intention. C'est pourquoi j'ai inclus des «objectifs» dans ma réponse. Si vous avez des objectifs différents, des réponses différentes pourraient leur convenir. Mon exemple n'est qu'un point de départ pour l'expérimentation. Il ne s'agit pas d'une solution plug & play. Il n'y a pas de «bénéfice», car les bénéfices dépendent de votre situation.
Anastazy
@ 1Cr18Ni9 Je pense que la mise en cache n'appartient pas au code des composants. Vous finirez par sur-concevoir quelque chose qui devrait être vraiment simple (récupérer des données et les lier à la vue). La mise en cache est déjà implémentée dans le navigateur. Vous pouvez en profiter en envoyant des en-têtes corrects depuis le serveur. Explication simple ici: csswizardry.com/2019/03/cache-control-for-civilians . Vous pouvez également consulter ServiceWorkers qui permettent au site Web de fonctionner même sans connexion Internet.
Anastazy
61

Vous ne devez pas utiliser les observateurs de composant pour écouter le changement d'état. Je vous recommande d'utiliser les fonctions getters, puis de les mapper à l'intérieur de votre composant.

import { mapGetters } from 'vuex'

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

Dans votre magasin:

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

Vous devriez pouvoir écouter les modifications apportées à votre boutique en utilisant this.myStatedans votre composant.

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

Gabriel Robert
la source
1
Je ne sais pas comment implémenter mapGetters. Pouvez-vous me montrer un exemple. Ca serait d'une grande aide. Je viens de mettre en œuvre la réponse GONG pour le moment. TY
Rbex
1
@Rbex "mapGetters" fait partie de la bibliothèque 'vuex'. Vous n'avez pas besoin de le mettre en œuvre.
Gabriel Robert
69
Cette réponse est tout simplement fausse. Il a en fait besoin de surveiller les propriétés calculées.
Juan
15
Le getter une fois appelé ne récupérera que l'état à ce moment. Si vous voulez que cette propriété reflète le changement d'état d'un autre composant, vous devez le surveiller.
C Tierney
3
Pourquoi "Vous ne devriez pas utiliser les observateurs de composants pour écouter le changement d'état"? Voici un exemple auquel vous ne pensez peut-être pas, si je veux regarder un jeton de l'état et quand il change pour rediriger vers une autre page. donc, dans certains cas, vous devez le faire. peut-être avez-vous besoin de plus d'expérience pour le savoir.
Shlomi Levi
48

C'est aussi simple que:

watch: {
  '$store.state.drawer': function() {
    console.log(this.$store.state.drawer)
  }
}
micah5
la source
6
C'est un putain de vue plus simple que n'importe laquelle des réponses ici ... y a-t-il un argument contre cela ...?
Inigo
19
C'est trop simple donc ça ne ressemble pas à js, js doit être plus compliqué.
digout
1
Ce serait encore plus simple si c'était le casfunction(n) { console.log(n); }
WofWca
2
Super cool. Intéressé également par tout inconvénient de cette approche. Jusqu'à présent, cela semble bien fonctionner.
namero999
1
Sérieusement. Cela semble beaucoup mieux que la réponse acceptée, qui nécessite des noms de fonction dupliqués dans watch et calculés. Un expert peut-il expliquer pourquoi ou pourquoi ne pas procéder de cette façon?
Exocentric
43

Comme mentionné ci-dessus, il n'est pas judicieux de regarder les changements directement en magasin

Mais dans de très rares cas, cela peut être utile pour quelqu'un, je vais donc laisser cette réponse. Pour d'autres cas, veuillez consulter la réponse @ gabriel-robert

Vous pouvez le faire à travers state.$watch. Ajoutez ceci dans votre createdméthode (ou là où vous en avez besoin) dans le composant

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.
    }
);

Plus de détails: https://vuex.vuejs.org/api/#watch

GONG
la source
3
Je ne pense pas que ce soit une bonne idée de regarder directement l'état. Nous devrions utiliser des getters. vuex.vuejs.org/en/getters.html#the-mapgetters-helper
Gabriel Robert
14
@GabrielRobert Je pense qu'il y a une place pour les deux. Si vous avez besoin de modifier de manière réactive les conditions du modèle en fonction, l'utilisation d'une valeur calculée avec mapState, etc. est logique. Mais sinon, comme pour un contrôle de flux uniforme dans un composant, vous avez besoin d'une surveillance complète. Vous avez raison, vous ne devriez pas utiliser d'observateurs de composants simples, mais l'état. $ Watch est conçu pour ces cas d'utilisation
roberto tomás
14
Tout le monde en parle, mais personne ne dit pourquoi! J'essaie de créer un magasin vuex synchronisé automatiquement avec une base de données lors des modifications. Je pense que les observateurs dans le magasin sont le moyen le plus simple! Qu'est-ce que tu penses? Toujours pas une bonne idée?
mesqueeb
16

Je pense que le demandeur veut utiliser la montre avec Vuex.

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

      },
      {
        deep:true
      }
      );
ouaisdixon
la source
13

C'est pour toutes les personnes qui ne peuvent pas résoudre leur problème avec les getters et qui ont réellement besoin d'un observateur, par exemple pour parler à des tiers non-vue (voir Vue Watchers sur quand utiliser les observateurs).

Les observateurs et les valeurs calculées du composant Vue fonctionnent également sur les valeurs calculées. Ce n'est donc pas différent avec 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);
        }
    }
}

s'il ne s'agit que de combiner l'état local et global, la documentation de mapState fournit également un exemple:

computed: {
    ...mapState({
        // to access local state with `this`, a normal function must be used
        countPlusLocalState (state) {
          return state.count + this.localCount
        }
    }
})
dube
la source
beau hack, mais trop fastidieux, tu ne trouves pas?
Martian2049
2
Ce n'est pas un hack si c'est dans la documentation, n'est-ce pas? Mais alors, ce n'est pas non plus un argument favorable pour vue / vuex
dube
8

si vous utilisez dactylographié, vous pouvez:

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

..

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

Zhang Sol
la source
Pourquoi exactement a-t-il été rejeté? Juste parce que la solution est pour vue-class-component et que le TO demandait d'anciens styles de vue-class? Je trouve le premier préférable. Peut-être que @Zhang Sol pourrait mentionner dans l'introduction, que c'est explicitement pour vue-class-component?
JackLeEmmerdeur
Notez bien pourquoi un décorateur dactylographié serait préférable à une solution native vue aussi simple que celle-ci: stackoverflow.com/a/56461539/3652783
yann_yinn le
6

Créez un état local de votre variable de magasin en surveillant et en définissant les changements de valeur. De telle sorte que la variable locale change pour le v-model d'entrée de formulaire ne mute pas directement la variable de stockage .

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

La meilleure façon d'observer les changements de magasin est d'utiliser mapGetterscomme l'a dit Gabriel. Mais il y a un cas où vous ne pouvez pas le faire, par mapGettersexemple, vous voulez obtenir quelque chose du magasin en utilisant le paramètre:

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

dans ce cas, vous ne pouvez pas utiliser mapGetters. Vous pouvez essayer de faire quelque chose comme ceci à la place:

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

Mais malheureusement, todoById ne sera mis à jour que si this.idest modifié

Si vous souhaitez mettre à jour vos composants dans ce cas, utilisez la this.$store.watch solution fournie par Gong . Ou gérez votre composant consciemment et mettez this.idà jour lorsque vous avez besoin de mettre à jour todoById.

Arseniy-II
la source
Merci. C'est exactement mon cas d'utilisation, et en effet le getter ne peut pas être regardé alors ...
pscheit
5

Si vous souhaitez simplement observer une propriété d'état et agir ensuite dans le composant en fonction des modifications de cette propriété, consultez l'exemple ci-dessous.

Dans store.js:

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

Dans ce scénario, je crée une booleanpropriété d'état que je vais changer à différents endroits de l'application comme ceci:

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

Maintenant, si je dois surveiller cette propriété d'état dans un autre composant, puis modifier la propriété locale, j'écrirais ce qui suit dans le mountedcrochet:

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

Tout d'abord, je configure un observateur sur la propriété state, puis dans la fonction de rappel, j'utilise le valuede cette propriété pour modifier le localProperty.

J'espère que ça aide!

Jakub A Suplicki
la source
3

Lorsque vous souhaitez regarder au niveau de l'état, cela peut être fait de cette façon:

let App = new Vue({
    //...
    store,
    watch: {
        '$store.state.myState': function (newVal) {
            console.log(newVal);
            store.dispatch('handleMyStateChange');
        }
    },
    //...
});
Andy
la source
Ce n'est pas une bonne idée de gérer le store.statechangement par dispatchaction d'état du composant car ce comportement ne fonctionne que si vous utilisez ce composant. Vous pourriez également terminer avec une boucle infinie. Regardez pour store.statechanger rarement utiliser, par exemple si vous avez un composant ou une page qui devrait effectuer une action basée sur des store.statemodifications qui ne pourraient pas être gérées à l'aide de mapState calculé uniquement là où vous ne pouvez pas comparer newValuevsoldValue
Januartha
@Januartha, quelle est votre suggestion à ce problème alors?
Billal Begueradj
@Andy oui bien sûr son travail. Je veux juste savoir pourquoi vous appelez store.dispatch? si vous voulez gérer le store.statechangement pour store' why not handle it inside store.mutations`?
Januartha
@BillalBEGUERADJ I prever dube solution est plus propre
Januartha
@Januartha, car il pourrait y avoir un appel ajax avant de faire une mutation, c'est pourquoi j'utilise en store.dispatchpremier. Par exemple, je veux obtenir toutes les villes d'un pays chaque fois que cela $store.state.countrychange, alors j'ajoute ceci à l'observateur. Ensuite, j'écrirais un appel ajax: dans store.dispatch('fetchCities')j'écris:axios.get('cities',{params:{country: state.country }}).then(response => store.commit('receiveCities',response) )
Andy
2

Vous pouvez utiliser une combinaison d' actions Vuex , de getters , de propriétés calculées et d' observateurs pour écouter les changements sur une valeur d'état Vuex.

Code HTML:

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

Code 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()}`)
    }
  }
})

Voir la démo de JSFiddle .

Amin NAIRI
la source
1

Vous pouvez également vous abonner aux mutations du magasin:

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

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

alliage
la source
Vous pouvez déclencher ceci dans le hook beforeMount () de votre composant puis filtrer les mutations entrantes avec une instruction if. par exemple if (mutation.type == "names / SET_NAMES") {... do something}
Alejandro
1

À l'intérieur du composant, créez une fonction calculée

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

Maintenant, le nom de la fonction calculée peut être regardé, comme

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

Les modifications apportées à l' vuexétat my_stateseront reflétées dans la fonction calculée myStateet déclenchent la fonction de surveillance.

Si l'état a my_statedes données imbriquées, l' handleroption aidera plus

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

Cela surveillera toutes les valeurs imbriquées dans le magasin my_state.

Rijosh
la source
0

Vous pouvez également utiliser mapState dans votre composant vue pour obtenir directement l'état du magasin.

Dans votre composant:

computed: mapState([
  'my_state'
])

my_stateest une variable du magasin.

Eugène Kulakov
la source
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: {}
})

semua bisa
la source
1
Bienvenue sur le site. Mettre uniquement un extrait de code ne suffit pas. Veuillez fournir quelques explications sur votre code.
pgk
0

J'ai utilisé de cette façon et cela fonctionne:

store.js:

const state = {
  createSuccess: false
};

mutations.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
}

Et dans votre composant où vous utilisez l'état du magasin:

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

Lorsque l'utilisateur soumet le formulaire, l'action STORE sera appelée, après le succès de la création, la mutation CREATE_SUCCESS est validée après cela. Tournez createSuccess est vrai, et dans le composant, l'observateur verra que la valeur a changé et déclenchera une notification.

isSuccess doit correspondre au nom que vous déclarez dans getters.js

Vuong Tran
la source