So your Vue application is growing larger and larger and it's getting harder and harder to manage all the disparate local states in your components. Wouldn't it be great if you had just a single point of truth for all of your data?
Well thankfully, Vue has Vuex, a state management pattern and library for Vue. Vuex makes it easy to maintain a one way data flow with your global state so that you can easily track and control any changes and maintain consistency throughout your application.
State is rendered to the components. If you want to change something, you call an action which makes a mutation, which changes the state. It may seem like actions and mutations should be the same step, but the difference is that mutations must be synchronous and actions can be asynchronous. So you can make API calls or talk to a database in an action. And when the action finishes you can tell a mutation to change the state with the new information.
Now that you have a basic understanding of a Vuex store, let's take a look at how we would implement it.
In our top level component, we need to import Vuex and set up a new store.
import Vue from 'vue'
import Vuex from 'vuex';
import App from './App.vue'
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
users: [],
},
});
new Vue({
render: h => h(App),
store,
}).$mount('#app')
We're going to be rendering a list of users that we're getting from a fake api called ReqRes. So we set up the initial user state as an empty array.
We're also going to need a way to mutate that list. So let's set up a mutation to load our users.
const store = new Vuex.Store({
state: {
users: []
},
mutations: {
loadUsers (state, payload){
state.users = payload.users;
},
},
});
Notice that our mutation takes the current state and a payload as its parameters. We need the state, so that we can mutate it, and the payload is how we're going to change it. Technically we don't need a payload, but if you are passing any information into the state you will end up using it.
Also notice we haven't made that api call yet, because a mutation has to be synchronous and we don't know how long it will take to make that external call.
So now let's add our action so we can get those users.
const store = new Vuex.Store({
state: {
users: []
},
mutations: {
loadUsers (state, payload){
state.users = payload.users;
},
},
actions: {
getUsers ({ commit }) {
axios({
method: 'get',
url: 'https://reqres.in/api/users',
})
.then(({ data }) => {
const users = data.data;
commit({
type: 'loadUsers',
users,
});
});
},
},
});
Actions take two parameters as well, but here we're only using the first, which is context, and destructuring commit
out of it. Context can be used to access the state or other actions if you want but we're only going to use commit
which is how you call a mutation. The second, optional, parameter is for if you want to pass any data to your action.
There are a few different ways to call commit. All of these are valid, but they all work a little differently.
commit('loadUsers', users);
commit('loadUsers', { users });
commit({
type: 'loadUsers',
users,
});
I prefer the final one, but if you want to read about them all check out Vuex's Documenation.
However you call commit, it finds the corresponding mutation and changes the state based on what you pass it. And then, because Vue is reactive, the state will trigger changes throughout the application where ever you are using it. So let's take a look at a component and how you can interact with the Vuex Store.
export default {
name: 'App',
computed: {
users() {
return this.$store.state.users;
},
},
methods: {
loadUsers: function () {
this.$store.dispatch({
type: 'getUsers',
});
},
},
};
Now that we have a Vuex Store, in any component we can access it with this.$store.state
as you see me doing in computed here. This links the local users
up with the store's. However, we can't just change it, we have to use actions.
So let's set up a method, loadUsers
, to handle calling our action. When we want to call an action we use dispatch
and to get a hold of our store's actions we just call this.$store.dispatch
and pass in the name of the action we want to call as the type.
Just like commit, there are several ways to call dispatch but I prefer the object style. If you want to check out the others look to Vuex's Documenation on actions.
So now whenever we call the loadUsers
method in our App component, it calls an action that talks to an api and gets back some users. Once it has the users it calls a mutation, which changes the users state in our store. Then our App component sees that users has changed and updates our page with the new list of users. One way data flow in action!
There's a lot more to Vuex as well, such as separating your store into different modules when it gets too large, or setting up 2 way binding with getters and setters. But hopefully I have piqued your interest enough for you to go check out Vuex and figure out how to use it for your application.
Top comments (0)