|
| 1 | +# Ações |
| 2 | + |
| 3 | +> Ações no Vuex são na verdade "action creators" em definições puras do Flux, mas eu achei esse termo mais confuso do que útil. |
| 4 | +
|
| 5 | +Ações são apenas funções que disparam mutações. Por convenção, as ações Vuex sempre esperam uma instância de um armazem (store) como primeiro parâmetro, seguido por parâmetros adicionais, que são opcionais: |
| 6 | + |
| 7 | +``` js |
| 8 | +// the simplest action |
| 9 | +function increment (store) { |
| 10 | + store.dispatch('INCREMENT') |
| 11 | +} |
| 12 | + |
| 13 | +// a action with additional arguments |
| 14 | +// with ES2015 argument destructuring |
| 15 | +function incrementBy ({ dispatch }, amount) { |
| 16 | + dispatch('INCREMENT', amount) |
| 17 | +} |
| 18 | +``` |
| 19 | + |
| 20 | +Isso pode parecer bobo a primeira vista: por que nós simplesmente não disparamos mutações diretamente? Bem, você se lembra que **mutações devem ser síncronas**? Ações não. Nós podemos realizar operações **assíncronas** dentro de uma ação: |
| 21 | + |
| 22 | +``` js |
| 23 | +function incrementAsync ({ dispatch }) { |
| 24 | + setTimeout(() => { |
| 25 | + dispatch('INCREMENT') |
| 26 | + }, 1000) |
| 27 | +} |
| 28 | +``` |
| 29 | + |
| 30 | +Um exemplo mais prático seria uma ação para realizar o checkout em um carrinho de compras, o que envolve **chamar uma API assíncrona** e **disparar múltiplas mutações**: |
| 31 | + |
| 32 | +``` js |
| 33 | +function checkout ({ dispatch, state }, products) { |
| 34 | + // save the current in cart items |
| 35 | + const savedCartItems = [...state.cart.added] |
| 36 | + // send out checkout request, and optimistically |
| 37 | + // clear the cart |
| 38 | + dispatch(types.CHECKOUT_REQUEST) |
| 39 | + // the shop API accepts a success callback and a failure callback |
| 40 | + shop.buyProducts( |
| 41 | + products, |
| 42 | + // handle success |
| 43 | + () => dispatch(types.CHECKOUT_SUCCESS), |
| 44 | + // handle failure |
| 45 | + () => dispatch(types.CHECKOUT_FAILURE, savedCartItems) |
| 46 | + ) |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +Note que ao invés de esperar um retorno ou passar callback para ações, o resultado de chamar uma API assíncrona é lidado ao disparar novas mutações. A política de boa prática vizinhança aqui é que **o único efeito colateral gerado ao chamar ações deve ser disparar mutações**. |
| 51 | + |
| 52 | +### Chamando Ações em Componentes |
| 53 | + |
| 54 | +Você deve ter percebido que uma função de ação não pode ser chamada diretamente sem referenciar uma instância do armazém. Tecnicamente, nós podemos invocar uma ação utilizando `action(this.$store)` dentro de um método, mas é melhor se nós pudermos expor versões de ações diretamente "ligadas" aos métodos dos componentes, e assim nós poderíamos facilmente referenciá-las dentro de templates. Nós podemos fazer isso utilizando a opção `vuex.actions`: |
| 55 | + |
| 56 | +``` js |
| 57 | +// inside a component |
| 58 | +import { incrementBy } from './actions' |
| 59 | + |
| 60 | +const vm = new Vue({ |
| 61 | + vuex: { |
| 62 | + getters: { ... }, // state getters |
| 63 | + actions: { |
| 64 | + incrementBy // ES6 object literal shorthand, bind using the same name |
| 65 | + } |
| 66 | + } |
| 67 | +}) |
| 68 | +``` |
| 69 | + |
| 70 | +O que o código acima faz é vincular a ação `incrementBy` a instância local do armazém do componente, e expô-lo no componente como se fosse um método da instância, acessado via `vm.incrementBy`. Qualquer argumento passado para o método `vm.incrementBy` será passado para a ação que importamos depois do primeiro argumento, que é nosso armazém. Então, ao chamar: |
| 71 | + |
| 72 | +``` js |
| 73 | +vm.incrementBy(1) |
| 74 | +``` |
| 75 | + |
| 76 | +é o mesmo que: |
| 77 | + |
| 78 | +``` js |
| 79 | +incrementBy(vm.$store, 1) |
| 80 | +``` |
| 81 | + |
| 82 | +Mas o benefício é que nós podemos vincular essa ação ao template do componente e utilizá-lo mais facilmente: |
| 83 | + |
| 84 | +``` html |
| 85 | +<button v-on:click="incrementBy(1)">increment by one</button> |
| 86 | +``` |
| 87 | + |
| 88 | +E você pode usar um nome diferente ao método ao vincular uma ação: |
| 89 | + |
| 90 | +``` js |
| 91 | +// inside a component |
| 92 | +import { incrementBy } from './actions' |
| 93 | + |
| 94 | +const vm = new Vue({ |
| 95 | + vuex: { |
| 96 | + getters: { ... }, |
| 97 | + actions: { |
| 98 | + plus: incrementBy // bind using a different name |
| 99 | + } |
| 100 | + } |
| 101 | +}) |
| 102 | +``` |
| 103 | + |
| 104 | +Agora a ação será conectada como `vm.plus` ao invés de `vm.incrementBy`. |
| 105 | + |
| 106 | +### Ações Inline |
| 107 | + |
| 108 | +Se uma ação é específica à um componente, você pode utilizar um atalho e definí-la diretamente no componente: |
| 109 | + |
| 110 | +``` js |
| 111 | +const vm = new Vue({ |
| 112 | + vuex: { |
| 113 | + getters: { ... }, |
| 114 | + actions: { |
| 115 | + plus: ({ dispatch }) => dispatch('INCREMENT') |
| 116 | + } |
| 117 | + } |
| 118 | +}) |
| 119 | +``` |
| 120 | + |
| 121 | +### Vinculando todas Ações |
| 122 | + |
| 123 | +Se você quiser vincular todas as ações compartilhadas: |
| 124 | + |
| 125 | +``` js |
| 126 | +import * as actions from './actions' |
| 127 | + |
| 128 | +const vm = new Vue({ |
| 129 | + vuex: { |
| 130 | + getters: { ... }, |
| 131 | + actions // bind all actions |
| 132 | + } |
| 133 | +}) |
| 134 | +``` |
0 commit comments