<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=705633339897683&amp;ev=PageView&amp;noscript=1">

Finishing the Vuex Duck: An alternative to mutation-types.js

John Bintz
by John Bintz
on April 24, 2018

Don't miss the latest from Tidelift

If you’re doing a large React project, you’re probably using Redux for state management. In the past when designing my own large React projects, after some initial trouble finding a good way to organize the parts of a Redux store slice (reducer, action creators, and constants), I settled on the Redux Duck pattern, which puts everything related to a piece of the store into the same file.

When I started working with Vue here at Tidelift, I found all the best Vuex examples followed that pattern very closely, except for one piece: where mutation constants live.

In the Vuex docs and in most examples or boilerplates, all of those mutation constants live in a single file, src/store/mutation-types.js. The idea is that, with all of these mutations in one place, a new developer can easily discover all the ways the app can manipulate store data.

Sign up to receive email updates from Tidelift

When you’re starting an app or have just synchronous store updates and you’re using commit directly, this is fine, as the file will be small. Once an app gets to a decent size, however, you’ll have more and more constants in this file, making searching for the right one harder. Also, if you move to exclusively dispatching actions (even if they’re synchronous actions) rather than committing mutations directly in components, there’s no need for a separate manifest file of constants. Each slice of the store is responsible for publishing actions that the components use to interact with the store, and constants become a way to avoid having to duplicate strings when calling and using mutations:

const state = () => ({

    whatever: false

})

const MY_ACTION = 'myAction'

export function myAction ({ commit, state }, whatever) {

    commit(MY_ACTION, whatever)

}

const actions = {

    myAction

}

const mutations = {

    [MY_ACTION] (state, data) {

        state.whatever = data

    }

}

export default {

    namespaced: true,

    state,

    actions,

    mutations

}

We removed the mutation-types.js file from the frontend app here at Tidelift, putting all of the constants alongside the actions, getters, and mutations for each part of the app. It’s made adding and modifying existing modules much easier. We already avoided directly mutating the store in components, so the only real tricky part was updating tests. Some of our tests tested mutations or complex actions and referenced the types in mutation-types.js, but the solution for this is easy, too: export the necessary constants from the store module and use those in your tests:

// src/store/modules/myModule.js

export const MY_ACTION = 'myAction'

// test/store/modules/myModule. To test.js

import module, { MY_ACTION } from ‘@/store/modules/myModule’

describe(‘mutation’, () => {

    it(‘should set the new state’, () => {

        const state = {}

        module.mutations[MY_ACTION](state, ‘hi’)

        expect(state.whatever).to.equal(‘hi’)

    })

})

If you're interested in learning more about Tidelift and the Tidelift Subscription (now supporting the Vue.js ecosystem), subscribe to updates and follow us on Twitter.

New Call-to-action