vue.js 2 come guardare i valori dei negozi da vuex


169

Sto usando vuexe vuejs 2insieme.

Sono nuovo vuex, voglio vedere un storecambiamento variabile.

Voglio aggiungere la watchfunzione nel miovue component

Questo è quello che ho finora:

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

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

Voglio sapere se ci sono cambiamenti nel my_state

Come posso guardare store.my_statenel mio componente Vuejs?


usa un plugin Vuejs che viene fornito con Chrome, ti tornerà utile
AKASH PANDEY il

Risposte:


206

Diciamo, ad esempio, che hai un cesto di frutta e ogni volta che aggiungi o rimuovi un frutto dal cesto, vuoi (1) visualizzare le informazioni sul conteggio della frutta, ma anche (2) vuoi essere avvisato di il conteggio dei frutti in qualche modo elegante ...

frutta-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>

Si noti che il nome della funzione watchnell'oggetto deve corrispondere al nome della funzione computednell'oggetto. Nell'esempio sopra il nome è count.

I valori vecchi e nuovi di una proprietà controllata verranno passati alla richiamata di controllo (la funzione di conteggio) come parametri.

Il negozio di cestini potrebbe apparire così:

frutta 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

Puoi leggere di più nelle seguenti risorse:


Mi chiedo solo cosa devo fare quando l' watchazione dovrebbe essere divisa in due passaggi: 1) Innanzitutto, controllando se i dati del desiderio sono memorizzati nella cache e se restituiscono solo i dati memorizzati nella cache; 2) Se la cache fallisce, ho bisogno di un'azione asincrona ajax per recuperare i dati, ma questo sembra essere il actionlavoro di. Spero che la mia domanda abbia un senso, grazie!
1Cr18Ni9

Qual è il vantaggio di questo, rispetto alla risposta di micah5, che imposta semplicemente un watcher nel componente, sul valore del negozio? Ha meno codice da mantenere.
Exocentrico

@Exocentric Quando ho scritto la risposta, la domanda non era chiara per me. Non esiste alcun contesto per cui è necessario guardare le proprietà. Pensala come: "Voglio guardare la variabile X, quindi posso fare Y." Probabilmente è per questo che la maggior parte delle risposte propone approcci così diversi. Nessuno sa quale sia l'intento. Ecco perché ho incluso "obiettivi" nella mia risposta. Se hai obiettivi diversi, una risposta diversa potrebbe adattarli. Il mio esempio è solo un punto di partenza per la sperimentazione. Non intende essere una soluzione plug & play. Non ci sono "benefici", perché i benefici dipendono dalla tua situazione.
Anastazy

@ 1Cr18Ni9 Penso che la memorizzazione nella cache non appartenga al codice del componente. Si finirà per progettare in modo eccessivo qualcosa che dovrebbe essere davvero semplice (recuperare i dati e vincolarli alla vista). La memorizzazione nella cache è già implementata nel browser. Puoi trarne vantaggio inviando le intestazioni corrette dal server. Spiegazione semplice qui: csswizardry.com/2019/03/cache-control-for-civilians . Puoi anche dare un'occhiata a ServiceWorkers che consente al sito Web di funzionare anche senza connessione a Internet.
Anastazy

61

Non utilizzare gli watcher del componente per ascoltare il cambiamento di stato. Ti consiglio di usare le funzioni di getter e di mapparle all'interno del tuo componente.

import { mapGetters } from 'vuex'

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

Nel tuo negozio:

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

Dovresti essere in grado di ascoltare tutte le modifiche apportate al tuo negozio utilizzando this.myState nel tuo componente.

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


1
Non so come implementare mapGetters. Puoi indicarmi un esempio. Sarebbe di grande aiuto. Ho appena implementato la risposta GONG al momento. TY
Rbex,

1
@Rbex "mapGetters" fa parte della libreria 'vuex'. Non è necessario implementarlo.
Gabriel Robert,

69
Questa risposta è sbagliata. In realtà ha bisogno di guardare le proprietà calcolate.
Juan,

15
Il getter chiamato una volta recupererà lo stato solo in quel momento. Se si desidera che la proprietà rifletta il cambio di stato da un altro componente, è necessario guardarlo.
C Tierney,

3
Perché "Non è necessario utilizzare gli osservatori dei componenti per ascoltare il cambiamento di stato"? Ecco un esempio a cui potresti non pensare, se voglio guardare il token dallo stato e quando cambia per reindirizzare a un'altra pagina. quindi, in alcuni casi è necessario farlo. forse hai bisogno di più esperienza per saperlo.
Shlomi Levi,

48

È semplice come:

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

6
Questa è una vista dannatamente più semplice di una qualsiasi delle risposte qui ... c'è qualche argomento contro farlo.?
Inigo,

19
È troppo semplice, quindi non sembra js, js deve essere più complicato.
scavare il

1
Sarebbe ancora più semplice se fossefunction(n) { console.log(n); }
WofWca,

2
Super cool. Interessato anche a qualsiasi inconveniente di questo approccio. Finora sembra funzionare bene.
namero999,

1
Sul serio. Questo sembra molto meglio della risposta accettata, che richiede nomi di funzioni duplicati in watch e calcolati. Può un esperto commentare perché o perché non farlo in questo modo?
Exocentrico

43

Come accennato in precedenza, non è una buona idea guardare i cambiamenti direttamente in negozio

Ma in alcuni casi molto rari può essere utile per qualcuno, quindi lascerò questa risposta. Per altri casi, vedere la risposta di @ gabriel-robert

Puoi farlo attraverso state.$watch. Aggiungi questo nel tuo createdmetodo (o dove hai bisogno che questo sia eseguito) nel componente

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

Maggiori dettagli: https://vuex.vuejs.org/api/#watch


3
Non penso sia una buona idea guardare direttamente lo stato. Dovremmo usare getter. vuex.vuejs.org/en/getters.html#the-mapgetters-helper
Gabriel Robert

14
@GabrielRobert Penso che ci sia un posto per entrambi. Se è necessario modificare in modo reattivo le condizioni del modello in base, ha senso utilizzare un valore calcolato con mapState, ecc. Altrimenti, come per un controllo uniforme del flusso in un componente, è necessario un controllo completo. Hai ragione, non dovresti usare watcher componente normale, ma lo stato. $ Watch è progettato per questi casi d'uso
roberto tomás

14
Tutti lo menzionano, ma nessuno dice perché! Sto cercando di costruire un negozio Vuex che si sincronizza automaticamente con un DB in caso di modifiche. Sento che gli osservatori nel negozio sono il modo più semplice! Cosa ne pensi? Non è ancora una buona idea?
mesqueeb,

16

Penso che il richiedente voglia usare watch con Vuex.

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

      },
      {
        deep:true
      }
      );

13

Questo è per tutte le persone che non riescono a risolvere il loro problema con i getter e che in realtà hanno davvero bisogno di un osservatore, ad esempio per parlare con cose di terze parti non vue (vedi Vue Watchers su quando usare gli osservatori).

I watcher e i valori calcolati del componente Vue funzionano entrambi anche su valori calcolati. Quindi non è diverso con 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);
        }
    }
}

se si tratta solo di combinare lo stato locale e globale, il documento di mapState fornisce anche un esempio:

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

bel trucco, ma troppo noioso, non credi?
Martian2049,

2
Non è un trucco se è nei documenti, vero? Ma poi, non è nemmeno un argomento a favore di vue / vuex
dube

8

se usi il dattiloscritto puoi:

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

..

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


Perché esattamente questo è stato downvoted? Solo perché la soluzione è per il componente di classe vue e il TO chiedeva vecchi stili di classe vue? Trovo il primo preferibile. Forse @Zhang Sol potrebbe menzionare nell'introduzione che questo è esplicitamente per il componente di classe vue?
JackLeEmmerdeur,

Nota sicuro perché un decoratore dattiloscritto sarebbe preferibile una soluzione nativa vue semplice come questo: stackoverflow.com/a/56461539/3652783
yann_yinn

6

Crea uno stato locale della variabile del tuo negozio osservando e impostando le variazioni di valore. In modo tale che la variabile locale cambi per il modello v di input del modulo non muta direttamente la variabile di archivio .

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

5

Il modo migliore per guardare i cambiamenti del negozio è usare mapGetterscome ha detto Gabriel. Ma c'è un caso in cui non puoi farlo attraverso, mapGettersad esempio, vuoi ottenere qualcosa dallo store usando il parametro:

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

in quel caso non puoi usare mapGetters. Invece potresti provare a fare qualcosa del genere:

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

Ma purtroppo todoById verrà aggiornato solo se this.idviene modificato

Se si desidera aggiornare il componente in tal caso, utilizzare la this.$store.watch soluzione fornita da Gong . Oppure gestisci il componente in modo consapevole e aggiorna this.idquando è necessario eseguire l'aggiornamento todoById.


grazie. Questo è esattamente il mio caso d'uso, e in effetti il ​​getter non può essere visto allora ...
pscheit

5

Se vuoi semplicemente guardare una proprietà dello stato e quindi agire all'interno del componente in base alle modifiche di tale proprietà, vedere l'esempio seguente.

In store.js:

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

In questo scenario, sto creando una booleanproprietà dello stato che ho intenzione di modificare in diversi punti dell'applicazione in questo modo:

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

Ora, se ho bisogno di guardare quella proprietà dello stato in qualche altro componente e quindi cambiare la proprietà locale, scriverei il seguente mountedgancio:

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

In primo luogo, sto impostando un watcher sulla proprietà state e poi nella funzione di callback uso la valueproprietà of per modificare il localProperty.

Spero possa essere d'aiuto!


3

Quando vuoi guardare a livello statale, puoi farlo in questo modo:

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

Non è una buona idea gestire il store.statecambiamento in base dispatchall'azione dello stato dal componente poiché questo comportamento funziona solo se si utilizza quel componente. Inoltre potresti finire con un ciclo infinito. Guarda per store.statecambiare raramente usa, per esempio se hai un componente o una pagina che dovrebbe fare qualche azione in base alla store.statemodifica che non può essere gestita usando mapState calcolata solo dove non puoi confrontare newValuevsoldValue
Januartha

@Januartha allora qual è il tuo suggerimento per questo problema?
Billal Begueradj,

@Andy sì, certo che funziona. Voglio solo notare perché chiami store.dispatch? se vuoi gestire il store.statecambiamento per store' why not handle it inside store.mutations`?
Januartha,

@BillalBEGUERADJ Prego che la soluzione del dube sia più pulita
Januartha,

@Januartha, perché potrebbe esserci una chiamata ajax prima di fare una mutazione, ecco perché uso store.dispatchprima. Ad esempio, desidero ottenere tutte le città da un Paese ogni volta che $store.state.countrycambia, quindi aggiungo questo al watcher. Quindi scriverei una chiamata Ajax: in store.dispatch('fetchCities')scrivo:axios.get('cities',{params:{country: state.country }}).then(response => store.commit('receiveCities',response) )
Andy,

2

È possibile utilizzare una combinazione di azioni Vuex , getter , proprietà calcolate e watcher per ascoltare le modifiche su un valore di stato Vuex.

Codice HTML:

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

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

Guarda la demo di JSFiddle .


1

Puoi anche iscriverti alle mutazioni del negozio:

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

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


Puoi attivarlo nell'hook beforeMount () del tuo componente, quindi filtrare le mutazioni in arrivo con un'istruzione if. ad es. if (mutation.type == "names / SET_NAMES") {... do qualcosa}
Alejandro

1

All'interno del componente, creare una funzione calcolata

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

Ora è possibile guardare il nome della funzione calcolata, ad esempio

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

Le modifiche apportate allo vuexstato my_statesi rifletteranno nella funzione calcolatamyState e attiveranno la funzione orologio.

Se lo stato my_stateha dati nidificati, l' handleropzione aiuterà di più

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

Questo controllerà tutti i valori nidificati nel negozio my_state.


0

Puoi anche usare mapState nel tuo componente vue per dirigere ottenere lo stato dal negozio.

Nel tuo componente:

computed: mapState([
  'my_state'
])

Dove si my_statetrova una variabile dal negozio.


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
Benvenuti nel sito. Inserire solo uno snippet di codice non è sufficiente. Fornisci alcune spiegazioni sul tuo codice.
PGK

0

Ho usato in questo modo e funziona:

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
}

E nel tuo componente in cui usi lo stato dal negozio:

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

Quando l'utente invia il modulo, l'azione STORE verrà chiamata, dopo aver creato con successo, la mutazione CREATE_SUCCESS viene commessa successivamente. Ruota createSuccess è vero e, nel componente, l'osservatore vedrà il valore è cambiato e attiverà la notifica.

isSuccess deve corrispondere al nome dichiarato in getters.js

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.