# Мутации

<div class="scrimba"><a href="https://scrimba.com/p/pnyzgAP/ckMZp4HN" target="_blank" rel="noopener noreferrer">Пройдите этот урок на Scrimba</a></div>

Единственным способом изменения состояния хранилища во Vuex являются мутации. Мутации во Vuex очень похожи на события: каждая мутация имеет строковый **тип** и **функцию-обработчик**. В этом обработчике и происходят, собственно, изменения состояния, переданного в функцию первым аргументом:

```js
const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment(state) {
      // изменяем состояние
      state.count++;
    }
  }
});
```

Вызывать функцию-обработчик напрямую — нельзя. Это больше похоже на обработку события: "Когда мутация типа `increment` инициирована, вызывается этот обработчик". Чтобы инициировать обработку мутации, необходимо вызвать `store.commit`, указав её тип:

```js
store.commit('increment');
```

### Мутации с нагрузкой

При вызове `store.commit` в мутацию можно также передать дополнительный параметр, называемый **нагрузкой (`payload`)**:

```js
// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
```

```js
store.commit('increment', 10);
```

В большинстве случаев нагрузка будет объектом, содержащим несколько полей. Запись мутаций в таком случае становится более описательной:

```js
// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
```

```js
store.commit('increment', {
  amount: 10
});
```

### Объектный синтаксис

Другой способ вызвать мутацию — это передать в commit единственный параметр, в котором `type` указан напрямую:

```js
store.commit({
  type: 'increment',
  amount: 10
});
```

При использовании объектной записи, объект передаётся в качестве нагрузки целиком, так что обработчик остаётся тем же самым:

```js
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
```

### Мутации следуют правилам реактивности Vue

Поскольку состояние хранилища Vuex — это реактивная переменная Vue, при возникновении мутации зависящие от этого состояния компоненты Vue обновляются автоматически. Кроме того, это значит, что мутации Vuex имеют те же самые подводные камни, что и реактивность в обычном Vue:

1.  Лучше инициализировать изначальное состояние хранилища, указав все поля в самом начале.

2.  При добавлении новых свойств объекту необходимо либо:

    * Использовать `Vue.set(obj, 'newProp', 123)`, или

    * Целиком заменить старый объект новым. Например, используя [синтаксис расширения объектов](https://github.com/tc39/proposal-object-rest-spread) можно написать так:

    ```js
    state.obj = { ...state.obj, newProp: 123 };
    ```

### Использование констант для обозначения типов мутаций

В различных вариантах реализации Flux этот подход используется весьма часто. Вынесите все константы с типами мутаций и действий в отдельный файл, чтобы было проще использовать линтеры и другие инструменты, а также чтобы дать читателям возможность с первого взгляда понять, какие мутации возможны в приложении:

```js
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION';
```

```js
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // "вычисляемые имена" из ES2015 позволяют использовать
    // константу в качестве имени функции
    [SOME_MUTATION] (state) {
      // здесь будет изменяться состояние
    }
  }
})
```

Тем не менее, использовать константы для указания типов мутаций совершенно необязательно, хотя это и может оказаться полезным в крупных проектах.

### Мутации должны быть синхронными

Нужно помнить одно важное правило: **обработчики мутаций обязаны быть синхронными**. Почему? Рассмотрим пример:

```js
mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}
```

Теперь представьте, что вы отлаживаете приложение и смотрите в лог мутаций в инструментах разработчика. Для каждой залогированной мутации devtools должен сохранить слепки состояния приложения "до" и "после" её наступления. Однако, асинхронный коллбэк внутри приведённой выше мутации делает это невозможным: мутация-то уже записана, и у devtools нет никакой возможности знать, что далее будет вызван коллбэк, а, значит, и инициируемые им изменения становится, по сути дела, невозможно отследить.

### Вызов мутаций в компонентах

Мутации можно вызывать из кода компонентов, используя `this.$store.commit('xxx')`, или применяя вспомогательный метод `mapMutations`, который проксирует вызовы `store.commit` через методы компонентов (для этого требуется наличие корневой ссылки на хранилище `$store`):

```js
import { mapMutations } from 'vuex';

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // `this.increment()` будет вызывать `this.$store.commit('increment')`

      // mapMutations также поддерживает нагрузку:
      'incrementBy' // `this.incrementBy(amount)` будет вызывать `this.$store.commit('incrementBy', amount)`
    ]),
    ...mapMutations({
      add: 'increment' // `this.add()` будет вызывать `this.$store.commit('increment')`
    })
  }
};
```

### О действиях

Привнесение асинхронности в мутации могло бы изрядно затруднить понимание логики программы. Например, если вызываются два метода, оба с асинхронными коллбэками, изменяющими состояние приложения — как предсказать, какой из коллбэков будет вызван первым? Именно поэтому концепции изменений и асинхронности рассматриваются по отдельности. Во Vuex **мутации — это синхронные транзакции**:

```js
store.commit('increment');
// все изменения состояния, вызываемые мутацией "increment",
// к этому моменту уже должны произойти.
```

Для обработки асинхронных операций существуют [Действия](actions.md).
