vue.js 2 cómo ver los valores de la tienda desde vuex

168

Estoy usando vuexy vuejs 2juntos.

Soy nuevo en vuex, quiero ver un storecambio variable.

Quiero agregar la watchfunción en mivue component

Esto es lo que tengo hasta ahora:

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

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

Quiero saber si hay algún cambio en el my_state

¿Cómo miro store.my_stateen mi componente vuejs?

raffffffff
fuente
use un complemento Vuejs que viene con cromo, será útil
AKASH PANDEY

Respuestas:

206

Digamos, por ejemplo, que tiene una canasta de frutas, y cada vez que agrega o quita una fruta de la canasta, desea (1) mostrar información sobre el recuento de frutas, pero también (2) desea que se le notifique el recuento de las frutas de una manera elegante ...

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>

Tenga en cuenta que el nombre de la función en el watchobjeto debe coincidir con el nombre de la función en el computedobjeto. En el ejemplo anterior, el nombre es count.

Los valores nuevos y antiguos de una propiedad vigilada se pasarán a la devolución de llamada de vigilancia (la función de conteo) como parámetros.

La tienda de cestas podría verse así:

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

Puedes leer más en los siguientes recursos:

Anastazy
fuente
Me pregunto qué debo hacer cuando la watchacción debe dividirse en dos pasos: 1) Primero, verificar si los datos deseados se almacenan en caché y si solo devuelve los datos almacenados en caché; 2) Si el caché falló, necesito una acción asíncrona ajax para obtener los datos, pero este parece ser el actiontrabajo de. Esperando que mi pregunta tenga sentido, ¡gracias!
1Cr18Ni9
¿Cuál es el beneficio de esto, sobre la respuesta de micah5, que solo establece un observador en el componente, en el valor de la tienda? Tiene menos código para mantener.
Exocéntrico
@Exocentric Cuando escribí la respuesta, la pregunta no estaba clara para mí. No hay contexto por el que es necesario mirar las propiedades. Piense en esto como: "Quiero ver la variable X, para poder hacer Y". Probablemente es por eso que la mayoría de las respuestas proponen enfoques muy diferentes. Nadie sabe cuál es la intención. Por eso incluí "objetivos" en mi respuesta. Si tiene objetivos diferentes, una respuesta diferente podría ajustarse a ellos. Mi ejemplo es solo un punto de partida para la experimentación. No está destinado a ser una solución plug & play. No hay "beneficio", porque los beneficios dependen de su situación.
Anastazy
@ 1Cr18Ni9 Creo que el almacenamiento en caché no pertenece al código del componente. Terminarás sobre-diseñando algo que debería ser realmente simple (obtener datos y vincularlos a la vista). El almacenamiento en caché ya está implementado en el navegador. Puede aprovecharlo enviando encabezados correctos desde el servidor. Explicación simple aquí: csswizardry.com/2019/03/cache-control-for-civilians . También puede echar un vistazo a ServiceWorkers que permiten que el sitio web funcione incluso sin conexión a Internet.
Anastazy
61

No debe usar los observadores de componentes para escuchar el cambio de estado. Le recomiendo que use funciones getters y luego las asigne dentro de su componente.

import { mapGetters } from 'vuex'

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

En su tienda:

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

Debería poder escuchar cualquier cambio realizado en su tienda utilizando this.myStatesu componente.

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

Gabriel Robert
fuente
1
No sé cómo implementar mapGetters. ¿Puedes señalarme un ejemplo? Sería de gran ayuda. Acabo de implementar la respuesta GONG en este momento. TY
Rbex
1
@Rbex "mapGetters" es parte de la biblioteca 'vuex'. No necesita implementarlo.
Gabriel Robert
69
Esta respuesta es simplemente incorrecta. Él realmente necesita ver las propiedades calculadas.
Juan
15
El captador una vez llamado solo recuperará el estado en ese momento. Si desea que esa propiedad refleje el cambio de estado de otro componente, debe observarla.
C Tierney
3
¿Por qué "No debe usar los observadores de componentes para escuchar el cambio de estado"? Aquí hay un ejemplo en el que quizás no se te ocurra, si quiero ver un token del estado y cuándo cambia para redirigirlo a otra página. Entonces, hay algunos casos en los que necesita hacer eso. tal vez necesites más experiencia para saber eso.
Shlomi Levi
46

Es tan simple como:

watch: {
  '$store.state.drawer': function() {
    console.log(this.$store.state.drawer)
  }
}
micah5
fuente
66
Esta es una vista más sencilla que cualquiera de las respuestas aquí ... ¿hay algún argumento en contra de hacer esto ...?
Iñigo el
19
Es demasiado simple, así que no parece js, js debe ser más complicado.
excavación
1
Sería aún más simple si lo fuerafunction(n) { console.log(n); }
WofWca
2
Super guay. Interesado también en cualquier inconveniente de este enfoque. Hasta ahora parece funcionar bien.
namero999
1
Seriamente. Esto parece mucho mejor que la respuesta aceptada, que requiere nombres de funciones duplicados en watch y computed. ¿Puede un experto comentar por qué o por qué no hacerlo de esta manera?
Exocéntrico
42

Como se mencionó anteriormente, no es una buena idea ver los cambios directamente en la tienda

Pero en algunos casos muy raros puede ser útil para alguien, por lo que dejaré esta respuesta. Para otros casos, consulte la respuesta de @ gabriel-robert

Puedes hacer esto a través de state.$watch. Agregue esto en su createdmétodo (o donde necesita que se ejecute) en el 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.
    }
);

Más detalles: https://vuex.vuejs.org/api/#watch

GONG
fuente
3
No creo que sea una buena idea mirar el estado directamente. Deberíamos usar getters. vuex.vuejs.org/en/getters.html#the-mapgetters-helper
Gabriel Robert
14
@GabrielRobert Creo que hay un lugar para ambos. Si necesita cambiar reactivamente las condiciones de la plantilla en función, tiene sentido usar un valor calculado con mapState, etc. Pero de lo contrario, como para un control de flujo uniforme en un componente, necesita un reloj completo. Tiene razón, no debe usar observadores de componentes simples, pero el estado. $ Watch está diseñado para estos casos de uso
roberto tomás
14
¡Todos lo mencionan, pero nadie dice por qué! Estoy tratando de construir una tienda vuex que se sincronice automáticamente con una base de datos ante cambios. ¡Siento que los observadores en la tienda son la forma más sencilla! ¿Qué piensas? ¿Todavía no es una buena idea?
mesqueeb
16

Creo que el autor de la pregunta quiere usar el reloj con Vuex.

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

      },
      {
        deep:true
      }
      );
yeahdixon
fuente
13

Esto es para todas las personas que no pueden resolver su problema con los captadores y que realmente necesitan un observador, por ejemplo, para hablar con cosas de terceros que no son vue (ver Vue Watchers sobre cuándo usar observadores).

Los observadores y los valores calculados del componente Vue también funcionan en valores calculados. Entonces no es diferente 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);
        }
    }
}

Si solo se trata de combinar el estado local y global, el documento de mapState también proporciona un ejemplo:

computed: {
    ...mapState({
        // to access local state with `this`, a normal function must be used
        countPlusLocalState (state) {
          return state.count + this.localCount
        }
    }
})
dube
fuente
buen truco, pero demasiado tedioso, ¿no te parece?
Martian2049
2
No es un truco si está en los documentos, ¿verdad? Pero tampoco es un argumento a favor de vue / vuex
dube
8

si usas mecanografiado puedes:

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

..

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

Zhang Sol
fuente
¿Por qué exactamente esto fue rechazado? ¿Solo porque la solución es para vue-class-component y TO estaba pidiendo viejos estilos de vue-class? Me parece preferible el primero. ¿Quizás @Zhang Sol podría mencionar en la introducción que esto es explícitamente para vue-class-component?
JackLeEmmerdeur
Tenga en cuenta por qué un decorador mecanografiado sería preferible a una solución nativa vue tan simple como esta: stackoverflow.com/a/56461539/3652783
yann_yinn
6

Cree un estado local de su variable de tienda observando y estableciendo cambios de valor. Tal que la variable local cambie para el modelo v de entrada de formulario no muta directamente la variable de tienda .

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

La mejor manera de ver los cambios en la tienda es usar mapGetterscomo dijo Gabriel. Pero hay un caso en el que no puede hacerlo, por mapGettersejemplo, si desea obtener algo de la tienda usando el parámetro:

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

en ese caso no puedes usar mapGetters. Puedes intentar hacer algo como esto en su lugar:

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

Pero desafortunadamente todoById se actualizará solo si this.idse cambia

Si desea la actualización de sus componentes en tal caso, utilice la this.$store.watch solución provista por Gong . O maneje su componente conscientemente y actualice this.idcuando necesite actualizar todoById.

Arseniy-II
fuente
gracias. Eso es exactamente mi caso de uso, y de hecho el comprador no se pueden ver a continuación ...
pscheit
5

Si simplemente quiere ver una propiedad del estado y luego actuar dentro del componente de acuerdo con los cambios de esa propiedad, consulte el siguiente ejemplo.

En store.js:

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

En este escenario, estoy creando una booleanpropiedad de estado que voy a cambiar en diferentes lugares de la aplicación de la siguiente manera:

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

Ahora, si necesito ver esa propiedad estatal en algún otro componente y luego cambiar la propiedad local, escribiría lo siguiente en el mountedenlace:

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

En primer lugar, estoy configurando un observador en la propiedad del estado y luego, en la función de devolución de llamada, uso la valuede esa propiedad para cambiarlocalProperty .

¡Espero que ayude!

Jakub A Suplicki
fuente
3

Cuando desee ver a nivel estatal, puede hacerlo de esta manera:

let App = new Vue({
    //...
    store,
    watch: {
        '$store.state.myState': function (newVal) {
            console.log(newVal);
            store.dispatch('handleMyStateChange');
        }
    },
    //...
});
Andy
fuente
No es una buena idea manejar el store.statecambio por dispatchacción de estado desde el componente, ya que este comportamiento solo funciona si usa ese componente. También podría terminar con un bucle infinito. Observe que el store.statecambio rara vez se usa, por ejemplo, si tiene un componente o una página que debería realizar alguna acción basada en el store.statecambio que no podría manejarse usando mapState calculado solo donde no puede comparar newValuevsoldValue
Januartha
@ Januartha, ¿cuál es tu sugerencia para este problema?
Billal Begueradj
@ Andy, por supuesto, es su trabajo. Solo quiero notar por qué llamas store.dispatch. si quieres manejar el store.statecambio para store' why not handle it inside store.mutations`?
Januartha
Prever @BillalBEGUERADJ I Dube solución es más limpia
Januartha
@Januartha, debido a que podría haber una llamada ajax antes de hacer una mutación, es por eso que la uso store.dispatchprimero. Por ejemplo, quiero obtener todas las ciudades de un país cada vez que $store.state.countrycambie, así que agrego esto al observador. Entonces escribiría una llamada ajax: en store.dispatch('fetchCities')escribo:axios.get('cities',{params:{country: state.country }}).then(response => store.commit('receiveCities',response) )
Andy
2

Se puede utilizar una combinación de Vuex acciones , captadores , propiedades calculadas y los observadores para escuchar a los cambios en un valor de estado Vuex.

Código HTML:

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

Código 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()}`)
    }
  }
})

Ver demostración de JSFiddle .

Amin NAIRI
fuente
1

También puede suscribirse a las mutaciones de la tienda:

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

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

aleación
fuente
Puede disparar esto en el gancho beforeMount () de su componente y luego filtrar las mutaciones entrantes con una instrucción if. por ejemplo, if (mutation.type == "names / SET_NAMES") {... haz algo}
Alejandro
1

Dentro del componente, cree una función calculada

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

Ahora se puede ver el nombre de la función calculada, como

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

Los cambios realizados en el vuexestado my_statese reflejarán en la función calculada myStatey activarán la función de reloj.

Si el estado my_statetiene datos anidados, entonces la handleropción ayudará más

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

Esto observará todos los valores anidados en la tienda my_state.

Rijosh
fuente
0

También puede usar mapState en su componente vue para dirigir el estado de la tienda.

En su componente:

computed: mapState([
  'my_state'
])

Donde my_statees una variable de la tienda.

Eugene Kulakov
fuente
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
fuente
1
Bienvenido al sitio. Poner solo un fragmento de código no es suficiente. Proporcione algunas explicaciones sobre su código.
PK
0

Utilicé de esta manera y funciona:

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
}

Y en su componente donde usa el estado de la tienda:

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

Cuando el usuario envíe el formulario, se llamará a la acción STORE , después de crearse correctamente , la mutación CREATE_SUCCESS se confirma después de eso. Gire createSuccess es verdadero, y en el componente, el observador verá que el valor ha cambiado y activará la notificación.

isSuccess debe coincidir con el nombre que declaras en getters.js

Vuong Tran
fuente