Mutações
A única maneira de realmente mudar de estado em um store Vuex é por confirmar (ou fazer commit de) uma mutação. As mutações do Vuex são muito semelhantes aos eventos: cada mutação tem uma String type e um handler. Na função manipuladora (ou handler) é onde realizamos modificações de estado reais e ele receberá o estado como o 1º argumento:
const store = createStore({
state: {
count: 1
},
mutations: {
increment (state) {
// muda o estado
state.count++
}
}
})
Você não pode chamar diretamente uma função manipuladora de mutação. Pense nisso mais como registro de evento: "Quando uma mutação com o type increment
é acionada, chame este handler." Para invocar uma função manipuladora de mutação (handler), você precisa chamar store.commit
com seu tipo (type):
store.commit('increment')
Confirmação (ou Commit) com Payload
Você pode passar um argumento adicional para o store.commit
, que é chamado de payload para a mutação:
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
Na maioria dos casos, o payload deve ser um objeto para que possa conter vários campos, e a mutação gravada também será mais descritiva:
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
Confirmação (ou Commit) Estilo-Objeto
Uma maneira alternativa de confirmar (ou fazer um commit de) uma mutação é usando diretamente um objeto que tenha uma propriedade type
:
store.commit({
type: 'increment',
amount: 10
})
Ao usar a Confirmação Estilo-Objeto, o objeto inteiro será passado como o payload para os manipuladores de mutação, portanto, a função manipuladora permanecerá a mesma:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
Usando Constantes para Declarar os Tipos de Mutação
É um padrão comumente visto usar constantes para declarar tipos de mutação em várias implementações do Flux. Isso permite que o código aproveite as ferramentas como os linters, e colocar todas as constantes em um único arquivo permite que seus colaboradores tenham uma visão geral das mutações possíveis em toda a aplicação:
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import { createStore } from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = createStore({
state: { ... },
mutations: {
// podemos usar o recurso de nome do dado computado do ES2015
// para usar uma constante como o nome da função
[SOME_MUTATION] (state) {
// muda o estado
}
}
})
Se usar constantes é em grande parte uma preferência - pode ser útil em grandes projetos com muitos desenvolvedores, mas é totalmente opcional se você não gostar deles.
Mutações Devem Ser Síncronas
Uma regra importante a lembrar é que as funções manipuladoras de mutação devem ser síncronas. Por quê? Considere o seguinte exemplo:
mutations: {
someMutation (state) {
api.callAsyncMethod(() => {
state.count++
})
}
}
Agora imagine que estamos depurando a aplicação e observando os logs de mutação do devtool. Para cada mutação registrada, o devtool precisará capturar os momentos "antes" e "depois" do estado. No entanto, o callback assíncrono dentro da mutação de exemplo acima torna isso impossível: o callback ainda não é chamado quando a mutação é confirmada (ou o commit da mutação é feito) e não há como o devtool saber quando o callback será realmente chamado - qualquer mutação de estado executada no callback é essencialmente impossível de rastrear!
Confirmando (ou fazendo Commits de) Mutações em Componentes
Você pode confirmar (ou fazer commit de) mutações em componentes com this.$store.commit('xxx')
, ou use o método auxiliar mapMutations
que mapeia métodos de componentes para chamadas store.commit
(requer injeção do store raiz):
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // mapeia `this.increment()` para `this.$store.commit('increment')`
// `mapMutations` also supports payloads:
'incrementBy' // mapeia `this.incrementBy(amount)` para`this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // mapeia `this.add()` para`this.$store.commit('increment')`
})
}
}
Vamos as Ações
A assincronicidade combinada com mutação de estado pode tornar seu programa muito difícil de entender. Por exemplo, quando você chama dois métodos com retornos de callbacks assíncronos que alteram o estado, como saber quando eles são chamados e qual retorno de callback foi chamado primeiro? É exatamente por isso que queremos separar os dois conceitos. No Vuex, mutações são transações síncronas:
store.commit('increment')
// qualquer mudança de estado que a mutação "increment" pode causar
// deve ser feito neste momento.
Para lidar com operações assíncronas, vamos apresentar Ações.