September 17, 2019

Почему мои Vuex хранилища имеют только один action и mutation

Перевод статьи Why all my Vuex stores have just one action and mutation and why yours should too. Я, как автор перевода, прошу несколько раз подумать над тем, использовать ли такие подходы в написании своего приложения или нет. Все подробности моего маленького обращения к вам подробно расписаны в комментариях к оригинальной статье. И также я рекомендую обратиться к этой статье.

По мере роста размера вашего приложения, в вашем Vuex растет количество действий (actions) и мутаций (mutations). Позвольте мне показать вам, как это свести к чему-то более управляемому.

Что такое Vuex

Vuex - это паттерн управления состоянием + библиотека для приложений на Vue.js. Он служит централизованным хранилищем данных для всех компонентов приложения с правилами, гарантирующими, что состояние может быть изменено только предсказуемым образом.

Как мы используем Vuex

Мы используем Vuex для обмена состоянием между всеми приложениями в нашем Factory Core Framework. Factory Core Framework представляет собой набор из приложений, в котором, в настоящее время, у нас есть девять Vuex хранилищ. Каждое хранилище содержит в себе состояние (state), действия (actions) и мутации (mutations).

Мы используем действия (actions) в одном из хранилищ для API запросов к backend'у. Как только нам приходят данные от API, мы используем мутации (mutations), чтобы сохранить их в состоянии, что в свою очередь позволяет любому компоненту получить доступ к этим данным.

Как вы можете себе представить, наши хранилища могут иметь очень большое количество действий для обработки запросов к API. Вот пример всех действий в одном из наших хранилищ Vuex.

В этом хранилище 16 действий. Теперь представьте, сколько всего действий у нашего Factory Core Framework, если у нас 9 хранилищ!

Упрощение наших действий

Все наши действия, в основном, выполняют одинаковую функцию.
Каждое действие делает следующее:

  • извлекает данные из API (включая полезную нагрузку, если это необходимо)
  • сохраняет данные в состояние (опционально)
  • возвращает ответ компоненту, которое вызвало действие

Чтобы перенести все в единый action, нам нужно ему передать:

  • endpoint
  • данные для endpoint (опционально)
  • ключ хранилища, в котором сохранить ответ сервера (опционально)

Наше текущее действие

Вот пример одного из наших действий.

async getLineWorkOrders({ rootState, commit }, payload) {
    try {
        let response = await axios.post(
            'api.factory.com/getLineWorkOrders',
            Object.assign({}, payload.body, {
                language: rootState.authStore.currentLocale.locale
            }),
            rootState.config.serviceHeaders
        )
        commit(
            'setCurrentWorkOrderNumber',
            response.data.currentWorkOrderNumber
        )
        return response.data
    } catch (error) {
        throw error
    }
}

В этом действии, при запросе к API по url api.factory.com/geteLineWorkOrders мы извлекаем данные в переменную response. После извлечения данных в переменную response, мы вызываем мутацию setCurrentWorkOrderNumber, в которую передается response.data.currentWorkOrderNumber. И, наконец, переменная response возвращаются компоненту, который и вызвал это самое действие.

Все наши действия имеют схожую друг на другу логику. Чтобы все наши действия объединить в одно действие, нам нужно в одном действии реализовать: отправку запроса к API, сделать возможность сохранять данные в состояние нашего хранилища и иметь возможность просто вернуть данные. После проведения рефакторинга, наше единое действие будет выглядеть следующим образом:

async fetchData({ rootState, commit }, payload) {
    try {
        let body = { language: rootState.authStore.currentLocale.locale }
        if (payload) {
            body = Object.assign({}, payload.body, body)
        }
        let response = await axios.post(
            `api.factory.com/${payload.url}`,
            body,
            rootState.config.serviceHeaders
        )
        if (payload.commit) {
            commit('mutate', {
                property: payload.stateProperty,
                with: response.data[payload.stateProperty]
            })
        }
        return response.data
    } catch (error) {
        throw error
    }
}

Это единственное действие будет обрабатывать все возможные вызовы. Если нам нужно отправить данные с помощью API, то это делается. Если нам нужно сохранить данные в состоянии, то действие тоже умеет это делать. Если нам не нужно сохранять данные в состояние нашего хранилища, то результат выполнения этого действия просто будет возвращен.

Используем одну мутацию

Ранее, для каждого действия, которому требовалось изменить состояние, мы создали новую мутацию. Мы заменим все наши мутации на одну мутацию.
Вот как будет выглядеть наша единственная мутация:

const mutations = {
    mutate(state, payload) {
        state[payload.property] = payload.with
    }
}

Вызов этой мутации происходит следующим образом:

commit('mutate', {
    property: <propertyNameHere>,
    with: <valueGoesHere>
})

Заключение

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

Послесловие

Подписывайся на нас в социальных сетях:
Vue.js

Nuxt.js

Наши друзья uWebDesign: