This article dives into Vuex, a Vue.js State Management Library. We'll look at the problems it was created to solve, the core concepts behind it, how to set it up, and, of course, use code examples at every step of the way.
Vuex is a state management library built by the Vue team for managing data in Vue.js applications. It provides a centralized way of managing data that is being used across the application and allows for easy read and write actions.
Vue promotes breaking down views into components. These components are reusable Vue instances that accept data, methods, etc. The data is where the state of the view is housed, while the method is what allows us to manipulate this state based on the user interactions on the view.
When a user clicks a button in a component, a method is being called which in turn performs an action on the said state while the said state updates the view about that change.
However, there are times when multiple components would need to share a state, or after a state is being modified in one component you need the the parent / child or sibling component to perform a followup action.
Depending on the position of this second component, you might decide to either use props
or the this.$parent
keyword to directly access the data or methods of the second component and perform the said action. But what if you have to do this for as many components as possible?
As the project gets bigger, you find yourself passing props about, and directly manipulating the DOM to access various components.
This approach becomes very tedious and also makes the code base hard to maintain or debug when you run into errors. This is were Vuex shines. It provides a global scope where you can put all states that would be shared among the various components.
It also gives our code more structure, makes debugging easy as we can use the DevTools to track errors that occur, and of course provides the reactivity that Vue brings to the table. Think of it as the windows in JavaScript — every component has access to it.
To install Vuex into a project, run the code below.
This will install the latest version of Vuex into your project. Once this is done, we need to initialize Vuex into the Vue app by creating our store.js file with the code below;
Now we can now proceed to creating a store. The store is essentially a reactive object that holds the application’s state, getters, mutations and actions.
The store is essentially the centralized state, which has some core concepts that allow us achieve this centralization. These concepts include:
This is a single object that contains the entire data. This is similar to the data
keyword in the single components structure, except that this state can be accessed from more than one component and, when this state gets updated, all components accessing it also receive this change. To create this object, we do the below:
To access the Vuex state in our Vue components, we would have to first import the store into the component by creating a computed property that would return the said state, then render the said state to the view.
Now, let’s import the store. There are two major ways of doing this:
this.$store
syntax:
Getters are pretty much computed properties for the Vuex store. They allow us to generate a new state based on the current state — for example, calculating how many items we have in the cart.
It also helps with reduction in duplication of code where ideally more than one component needs this data and we would ordinarily have to do our manipulating in each component. With getters we can do it once and reference anywhere.
To create a getter, we do the below:
Next, we access the getter from our Vue component by doing below:
Now, whenever an item gets added to the cart, the total number of items in the cart gets updated automatically.
Mutations are the only way in which we can update our Vuex state. They perform one task and one task only: to set a state. It is a function that takes two arguments, the state and a payload, where the payload is not necessarily required.
The payload is simply the data that would be used to update the state. Mutations are synchronous, and as such we can’t perform asynchronous task in them.
Now, let’s add a mutation to our code:
Next, we need to update the state from our Vue component, and, to do that, we would need to commit the mutation.
Now anytime a user enters a value into the input and clicks on the submit button, the item gets added to cart and the total number of items in the cart gets updated on the view.
Actions are similar to mutations, but instead of mutating the state they commit mutations. They are asynchronous and, as such, allow us to perform asynchronous tasks; when these tasks are completed, we proceed to commit a mutation, which in turn updates the state.
To showcase actions, we would proceed to submit the cart items to an API.
Looking at the code above, we created an action named checkout
that accepts two things:
commit
: which allows us to call the commit method inside our actions
requestObject
: which allows us to pass data into action
Moving into the action, we made an asynchronous call to an API, and then passed the requestObject
to the API. On success, we logged the response and then proceeded to clear the cart state, but first we had to create an emptyCart
mutation, whose singular task is to empty the cart state.
Now that we have seen how to create actions, we proceed to triggering that action. To trigger an action, Vuex provides us with a dispatch
command.
this.$store.dispatch('actionName', payload);
Let’s add an action into our code and dispatch it from the view:
Based on the code above, we created a checkout button in the view, and created a checkout method that checks if the cart is not empty before trying to dispatch the action that submits the items.
This works, but something is missing. You might wonder what that is? We have been able to dispatch an action, but we don’t know if this action was successful or not.
Did the API call fail? Did it pass? How can I get this information so I can notify the user? Actions can handle Promises and can also return a Promise.
Modifying our example code to return a Promise:
Now, we can use the value returned to update the user on the state of things in the view as follows:
Actions also allow you to dispatch multiple actions (i.e. an action can dispatch one or more other actions). All you have to do is pass dispatch
as an argument and you would be able to dispatch other actions inside your action.
Right now, we have all our state, getters, mutations, and actions all in one file, the store.js file. Depending on how large our code base gets, this file can tend to become very big, and it just makes sense for us to split this into separate files.
store/
--| store.js
--| state.js
--| getters.js
--| mutations.js
--| actions.js
Now, our store looks like this:
Vuex also provides us with modules, where we can further structure or break down our store into modules of smaller stores. Each module will have its own state, getters, mutations, and actions.
This works by grouping related states, getters, mutations, and actions into a module. It is mostly useful when we have a large-scale app and the store is bloated with lots of code.
Refactoring our store into a module, we will create a file called cart.js and proceed to break out all our states, mutations, and actions in our store relating to the cart like below:
Next, we import and register it to our main store.
Finally, our code structure would look like this:
store/
--| store.js
--| state.js
--| getters.js
--| mutations.js
--| actions.js
--| modules/
--| cart.js
Vuex creates a store, which consists of states, getters, mutations and actions. To update or change a state, you have to commit a mutation.
To perform an asynchronous task, you need an action. Actions are dispatched, which on success commit a mutation which mutates a state, thus updating the view.
Want to learn about creating great user interfaces with Vue? Check out Kendo UI for Vue, our complete UI component library that allows you to quickly build high-quality, responsive apps. It includes all the components you’ll need, from grids and charts to schedulers and dials.
Nosa is a Frontend Developer with a passion for turning designs into beautiful interactive interfaces.