As ações são semelhantes às mutações, as diferenças são as seguintes:
As ações podem conter operações assíncronas arbitrárias. Vamos registrar uma simples ação:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Os manipuladores de ação recebem um objeto de contexto que expõe o mesmo conjunto de métodos / propriedades na instância do store, para que você possa chamar context.commit
para confirmar uma mutação ou acessar o estado e os getters através do context.state
e do contexto.getters
.
Veremos por que esse objeto de contexto não é a própria instância do store quando apresentamos Módulos mais tarde.
Na prática, muitas vezes usamos ES2015 desestruturação de argumentos para simplificar um pouco o código (especialmente quando precisamos usar commit várias vezes):
actions: {
increment ({ commit }) {
commit('increment')
}
}
As ações são acionadas com o método store.dispatch
:
store.dispatch('increment')
Isso pode parecer óbvio à primeira vista: se quisermos incrementar a contagem, por que não chamamos store.commit('increment')
diretamente? Você se lembra que as mutações devem ser síncronas? As ações não. Podemos executar operações assíncronas dentro de uma ação:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
As ações suportam o mesmo formato de carga útil e despacho de estilo de objeto:
// dispatch com payload
store.dispatch('incrementAsync', {
amount: 10
})
// dispatch com objeto
store.dispatch({
type: 'incrementAsync',
amount: 10
})
Um exemplo mais prático de ações reais seria uma ação para fazer check-out de um carrinho de compras, que envolve chamar uma API assíncrona e confirmar múltiplas mutações:
actions: {
checkout ({ commit, state }, products) {
// salva os itens que estão no carrinho
const savedCartItems = [...state.cart.added]
// enviar solicitação de checkout
// limpa o carrinho
commit(types.CHECKOUT_REQUEST)
// a API do store aceita um callback bem-sucedido e um callback com falha
shop.buyProducts(
products,
// callback em caso de sucesso
() => commit(types.CHECKOUT_SUCCESS),
// callback em caso de falha
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}
Observe que estamos realizando um fluxo de operações assíncronas e gravando os efeitos colaterais (mutações de estado) da ação confirmando-os.
Você pode despachar ações em componentes com this.$store.dispatch('xxx')
, ou usar o auxiliar mapActions
que mapeia métodos de componente para chamadas do store.dispatch
(esta ação requer injeção do store
na instância raiz):
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // mapeia `this.increment()` para `this.$store.dispatch('increment')`
// `mapActions` também suporta payloads:
'incrementBy' // mapeia `this.incrementBy(amount)` para `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // mapeia `this.add()` para `this.$store.dispatch('increment')`
})
}
}
As ações geralmente são assíncronas, então, como sabemos quando uma ação é realizada? E, o mais importante, como podemos compor ações múltiplas em conjunto para lidar com fluxos assíncronos mais complexos?
A primeira coisa a saber é que o store.dispatch
pode manipular o Promise retornado pelo manipulador de ação acionado e também retorna Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
Agora, é possivel:
store.dispatch('actionA').then(() => {
// ...
})
E em outra ação também:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
Por fim, se usarmos async / await, podemos compor nossas ações como esta:
// Assumindo que `getData ()` e `getOtherData ()` retornam Promessas
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // esperar que `actionA` termine
commit('gotOtherData', await getOtherData())
}
}
É possível para um
store.dispatch
desencadear vários manipuladores de ação em diferentes módulos. Neste caso, o valor retornado será uma Promise que resolve quando todos os manipuladores desencadeados foram resolvidos.