Могу ли я вызвать фиксацию из одной из мутаций в магазине Vuex

У меня есть магазин vuex, например:

import spreeApi from '../../gateways/spree-api'
// initial state
const state = {
  products: [],
  categories: []
}

// mutations
const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   commit('SET_CATEGORIES')
 },
 SET_CATEGORIES: (state) => {
   state.categories = state.products.map(function(product) { return product.category})
 }

}

const actions = {
 FETCH_PRODUCTS: (state, filters) => {
   return spreeApi.get('products').then(response => state.commit('SET_PRODUCTS', response))
 }
}

export default {
  state,
  mutations,
  actions
}

Я хочу вызвать мутацию: SET_CATEGORIES из мутации: SET_PRODUCTS, но это дает мне ошибку:

projectFilter.js: 22 Uncaught (в обещании) ReferenceError: фиксация не определена (…)

Какой должен быть правильный способ сделать это. Я пробовал store.commit и this.commit, но они также дали похожие ошибки.


person Saurabh    schedule 08.11.2016    source источник
comment
Связанная (закрытая) проблема: github.com/vuejs/vuex/issues/907   -  person Amir Ali Akbari    schedule 02.02.2018
comment
Привет @Saurabh, я протестировал ответ Кубивамы Адриена, и кажется, что в нем есть то, что вам нужно, может быть, протестируйте его и обновите этот форум последним ответом? Спасибо!   -  person Irfandy Jip    schedule 27.09.2019
comment
Почему бы не использовать действие и не вызвать внутри него несколько мутаций?   -  person August    schedule 19.10.2020


Ответы (13)


Когда вы уже делаете мутацию, нет возможности commit другую мутацию. Мутация - это синхронный вызов, который меняет состояние. В рамках одной мутации вы не сможете совершить другую мутацию.

Вот справочник по API для Vuex: https://vuex.vuejs.org/en/api.html

Как видите, обработчик мутации получает только state и payload, не более того. Следовательно, вы получаете commit как undefined.

В приведенном выше случае вы можете установить ПРОДУКТ и КАТЕГОРИИ как часть того же обработчика мутации, что и одна фиксация. Вы можете попробовать, работает ли следующий код:

// mutations
const mutations = {
    SET_PRODUCTS_AND_CATEGORIES: (state, response) => {
        state.products = response.data.products
        state.categories = state.products.map(function(product) { return product.category})
    },
    // ...
}

РЕДАКТИРОВАТЬ: См. ответ ниже, предоставленный Дэниелом С. Дебоером. Правильный метод - совершить две мутации из одного действия, как описано в его ответе.

person Mani    schedule 08.11.2016
comment
Мне интересно, почему нельзя совершать мутацию из другой мутации? Некоторые мутации могут быть настолько атомарными, что их должны использовать более крупные функции мутации. В текущей версии должно быть много дублирования кода из-за этого ограничения. - person Asqan; 16.02.2017
comment
Да, возможна фиксация от другой мутации без побочных эффектов. Но отладка неправильного изменения состояния, когда у нас есть вызывающие друг друга мутации, приведет к очень плохому опыту разработчика. Думаю, поэтому это не рекомендуется и не разрешается. Но, к счастью, у нас есть это простое решение - мы всегда можем определить одну мутацию, которая вызывает несколько изменений состояния, как показано в этом вопросе / ответе. - person Mani; 16.02.2017
comment
ИМХО, ответ ниже (совершить две вещи из одного действия). Он чище и читабельнее. - person JCKödel; 21.01.2018
comment
@ JCKödel Я согласен, правильный ответ должен быть приведенным ниже (зафиксировать две мутации из действия), предоставленным Дэниелом. Я сделаю пометку в своем ответе выше, чтобы найти лучший ответ ниже. - person Mani; 22.01.2018
comment
@Mani, почему это не предпочтительнее, чем ответ Дэни? Я думал, что это семантически более правильно с точки зрения Vuex, поскольку действие предназначено в первую очередь для асинхронных операций. - person Alex Napitupulu; 10.04.2018
comment
@AlexNapitupulu Обычно у нас есть обработчик мутаций, который делает только одну простую и понятную вещь за раз - либо устанавливает продукты, либо категории, но не то и другое одновременно. Исходя из требований этого вопроса, нам нужно делать две вещи одновременно. Лучше превратить эту сложность в действие, а не в мутацию. Мутации более внутренние по отношению к хранилищу и ближе к фактическим данным, тогда как действия - это те, которые доступны компонентам. Поэтому логично иметь действие, которое делает именно то, что компонент хочет. - person Mani; 11.04.2018
comment
@AlexNapitupulu Что касается асинхронных операций, оба этих подхода эквивалентны, поскольку действие в целом является асинхронным. Если действие запускается асинхронно, не имеет значения, фиксирует ли оно две мутации одну за другой или вызывает специальный обработчик мутации, который выполняет две операции в хранилище. Это выбор дизайна, но я думаю, что лучше сохранить эту сложность ближе к компоненту (в действии), чем обрабатывать ее дальше в мутации. - person Mani; 11.04.2018

Если вам абсолютно необходимо совершить две мутации, почему бы не сделать это из действия? Действия не обязательно должны выполнять асинхронные операции. Вы можете деструктурировать метод фиксации в своем действии так же, как и с таким состоянием:

commitTwoThings: ({commit}, payload) => {
  commit('MUTATION_1', payload.thing)
  commit('MUTATION_2', payload.otherThing)
}
person Daniel S. Deboer    schedule 31.03.2017
comment
Да, это должен быть ответ. - person Thiago Yoithi; 24.05.2017
comment
Почему бы и нет? .. С помощью vue-native-websocke вам нужно обрабатывать данные с сервера ws в мутации (SOCKET_ONMESSAGE) ... И у них тоже нет способа вызвать действие из мутации. - person Constantin De La Roche; 15.01.2020

Для записи. Чтобы вызвать другие мутации из метода мутации, сделайте это следующим образом:

const mutations = {
    mutationOne(state, payload){
        this.commit("mutationTwo", payload)
    },
    mutationTwo(state, payload){
        console.log("called from another mutation", payload)
    }
}
person Kubwimana Adrien    schedule 22.02.2019
comment
Это новая функция Vuex? Удивлен тем, что кому-то понадобилось два с половиной года, чтобы указать явно правильный ответ. - person Michael Hays; 27.08.2019
comment
Я не совсем уверен, считается ли это лучшей практикой. Однако этот ответ дает прямой ответ на вопрос. без предоставления альтернатив, которые были предложены другими через action. Я протестировал его, и, похоже, он работает хорошо. Я думаю, что этот ответ должен быть на первом месте. Это решение уже обсуждалось в Vuex Github - добавьте возможность вызова мутации из другой мутации # 907 если кому интересно прочитать больше доказательств. - person Irfandy Jip; 27.09.2019
comment
Кстати, если ваш modules находится в пространстве имен, даже если он находится в том же файле, вы должны получить к нему доступ через this.commit('modulesName/mutationName') на всякий случай, если кому-то интересно. Если вам нужна дополнительная информация, это всегда хорошее напоминание о том, чтобы просто сделать console.log(this) внутри мутации, кажется, что он содержит тот же экземпляр, что и Vue, к которому вы также можете получить доступ $route оттуда. - person Irfandy Jip; 27.09.2019
comment
Это лучший ответ. Это работает. - person drtob; 27.03.2021

Чтобы совместно использовать код между мутациями, вы должны создать новую функцию, выполняющую работу, которую вы затем можете использовать повторно. К счастью, мутации - это просто старые функции, и мы можем передавать параметр state, как нам нравится, так что это довольно легко сделать.

Например:

const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   setCategories(state)
 },
 SET_CATEGORIES: (state) => {
   setCategories(state)
 }
}

function setCategories(state) {
  state.categories = state.products.map(product => product.category)
}
person Daniel Buckmaster    schedule 25.02.2017
comment
Я думаю, это дает ошибку о состоянии цепочки вне мутации - person Drew Baker; 13.09.2019
comment
Что заставляет вас думать, что? Пока вы вызываете setCategories только из мутации, все будет в порядке. - person Daniel Buckmaster; 20.09.2019
comment
У меня это сработало, но я оставил реализацию функции как мутацию и просто вызвал из функции mutations.setCategories (состояние). - person jason; 28.12.2020

И если у меня есть общий код, который влияет на состояние между несколькими мутациями, я должен дублировать один и тот же код для всех моих мутаций? Или есть способ лучше?

person Nacho    schedule 05.12.2016
comment
Оглушающая тишина. Я бы тоже хотел получить ответ на это. - person daninthemix; 19.01.2017
comment
@Nacho, посмотри ответ, который я только что создал. Это не идеально, но это лучшее, что я знаю - person Daniel Buckmaster; 25.02.2017
comment
Я согласен с Дэниелом. Функции - это способ повторно использовать код. - person cstricklan; 25.03.2017
comment
И заверните их в mutation-helpers.js модуль или используйте действия. Хотя в некоторых случаях введение действия является излишним. - person Steven Pribilinskiy; 09.08.2017
comment
Да, мутация - это просто функция, которая принимает состояние в качестве аргумента и изменяет его. Вы можете объявить столько вспомогательных функций, сколько захотите, и использовать их повторно. - person Chris Williamson; 13.08.2017
comment
Пожалуйста, отправляйте ответ только тогда, когда у вас есть ответ. Если вы хотите что-то спросить, создайте новую беседу. - person Pablo; 26.01.2018

Читая документацию Vuex о действиях, становится совершенно ясно, для чего они созданы.

  • совершать мутации вместо изменения состояния
  • может содержать произвольные асинхронные операции

Действия могут (не должны) содержать асинхронный код. На самом деле следующий пример верен

increment (context) {
   context.commit('increment')
}

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

person Wanny Miarelli    schedule 25.01.2018

В вашем случае вам следует рассмотреть возможность наличия только одной мутации, а именно SET_PRODUCTS.

// mutations
const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   state.categories = state.products.map(function(product) { return product.category})
 }
}

У вас никогда не должно быть необходимости вызывать SET_CATEGORIES отдельно. Думаю об этом! Категории могут видоизменяться только при изменении продуктов. И продукты можно изменить только через SET_PRODUCTS.

person jiv-e    schedule 16.03.2017

Изменить: я наткнулся на очень похожую проблему, и решением для меня было использование получателя vuex: https://vuex.vuejs.org/en/getters.html
Ваши категории на самом деле являются "вычисленной" версией ваших продуктов. Наличие категорий в качестве получателя позволяет синхронизировать их с продуктами и позволяет избежать дублирования данных в вашем магазине.

Чтобы ответить на вопрос в заголовке, я оставляю свой первоначальный ответ.
Альтернатива решению Дэниела Бакмастера:

const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   this.SET_CATEGORIES(state)
 },
 SET_CATEGORIES: (state) => {
   state.categories = state.products.map(product => product.category)
 }
}

Как видите, вы можете напрямую вызвать саму мутацию. (как сказал Дэниел, в конце концов, это просто простые функции)
Я считаю, что это более подходящий ответ на исходный вопрос: это реальный способ составления мутаций без дублирования кода или дополнительных функций

person Guillaume Meral    schedule 19.12.2017
comment
Вызов функции мутации не то же самое, что фиксация мутации. - person Emile Bergeron; 25.01.2018
comment
Не могли бы вы уточнить? Это действительно проблема, поскольку мы вызываем ее из другой мутации? - person Guillaume Meral; 29.01.2018
comment
Например, обработчик для зарегистрированных плагинов не будет вызываться для второй мутации. Это означает, что инструменты Vue dev не будут отображать вторую мутацию в списке мутаций, к которым вы можете путешествовать во времени. - person Emile Bergeron; 29.01.2018
comment
Но вариант использования - это одна мутация, которая делает две вещи. Я понимаю вашу точку зрения, но в этом случае вы не хотите иметь состояние магазина, в котором есть десинхронизированные списки продуктов и категорий. - person Guillaume Meral; 29.01.2018
comment
Речь идет о совершении двух различных мутаций. Мутация, которая приводит к множеству изменений, - это еще одна проблема проектирования, уже рассмотренная в ответе Дэниела. - person Emile Bergeron; 29.01.2018
comment
Хм, да, именно это я имел в виду, говоря «Альтернатива решению Дэниела Бакмастера». Это мой первый ответ на StackOverflow, возможно, мне следовало вместо этого прокомментировать его решение. - person Guillaume Meral; 29.01.2018
comment
Вы пока не можете комментировать посты других людей, так как это требует 50 репутации. В этом случае ответ допустим: Мне это просто не нравится. Вы должны оставить его, чтобы документировать все возможные способы, и кому-то это может понравиться и проголосовать за него (голос за 10 баллов, против -2 для дв). Не сдавайтесь ТАК! - person Emile Bergeron; 29.01.2018
comment
Действительно ! Этот разговор заставил меня понять, что решением этой проблемы также может быть категория в качестве получателя. Я отредактировал свой ответ, чтобы немного пояснить. Пожалуйста, дайте мне знать, что вы думаете, и если я должен немного подробнее описать свои рассуждения. - person Guillaume Meral; 29.01.2018
comment
У меня не работало в браузере. Только в юнит-тестах. - person Amio.io; 02.06.2020

Сначала назначьте кнопку Vue переменной: В main.js:

  export const app = new Vue({  
  router,
  vuetify,
  store,....

Затем импортируйте переменную app в файл js, в котором вы определяете мутацию: В modules.js:

import { app } from "../../main";

Теперь вы можете использовать его как app. $ store.commit:

mutations: {
[AUTH_SET_TOKEN]: () => {
app.$store.commit(USER_SUCCESS, params );
},...
person Ali KOCA    schedule 11.05.2020

Я предпочитаю вызывать mutations.SET_CATEGORIES(state) вместо: - вызова двух разных коммитов из искусственного действия - или выполнения commit() внутри мутации, поскольку это затрудняет модульное тестирование.

const mutations = {
 SET_PRODUCTS: (state, response) => {
   state.products = response.data.products
   mutations.SET_CATEGORIES(state)
 },
 SET_CATEGORIES: (state) => {
   state.categories = state.products.map(product => product.category)
 }
}

Я считаю, что вам не нужно видеть SET_CATEGORIES в VueToolbox. Путешествие во времени в любом случае должно работать. Пожалуйста, поправьте меня, если я ошибаюсь.

person Amio.io    schedule 02.06.2020

думаю

вызов мутации из другой мутации - плохая идея из-за сложности отладки состояния и компонентов

const mutations = {
    mutationOne(state, payload){
        this.commit("mutationTwo", payload)
    },
    mutationTwo(state, payload){
        console.log("called from another mutation", payload)
    }
}

но вы можете написать простую функцию, а функцию можно использовать повторно

function mysecondfn(state,payload){
{
// do your stuff here
}


const mutations = {
    mutationOne(state, payload){
mysecondfn(state,payload)
     },

}
person ßãlãjî    schedule 15.07.2020

другое решение, которое работает для меня:

this._mutations.mutationFunction[0]()
person JeffNhan    schedule 23.04.2021

import spreeApi from '../../gateways/spree-api'
// initial state
const state = {
  products: [],
  categories: []
}

// mutations
const mutations = {
 SET_PRODUCTS: (state, {response,commit}) => { // here you destructure the object passed to the mutation to get the response and also the commit function
   state.products = response.data.products
   commit('SET_CATEGORIES') // now the commit function is available
 },
 SET_CATEGORIES: (state) => {
   state.categories = state.products.map(function(product) { return product.category})
 }

}

const actions = {
 FETCH_PRODUCTS: ({commit}, filters) => { // here you destructure the state to get the commit function
   return spreeApi.get('products').then(response => commit('SET_PRODUCTS', {response,commit})) // here you pass the commit function through an object to 'SET_PRODUCTS' mutation
 }
}

export default {
  state,
  mutations,
  actions
}

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

person Andrei    schedule 19.02.2019
comment
Пожалуйста, добавьте пояснение к вашему коду: что именно нужно изменить и почему? Имейте в виду, что OP должен уметь извлекать уроки из вашего ответа. - person Nico Haase; 19.02.2019