Blogs

How to use Vuex

Category
Software development
How to use Vuex

Enterprise applications are complex because multiple parts of the state are used in many different components. State management libraries are often used for interactions between individual components. Vuex state management pattern and the library are available for the Vue.js framework. It emerged similar to the Redux state management library. Like in Redux, Vuex is the “single source of truth” principle that is present – one app, one store. However, Vuex also does not have a dispatcher, unlike Flux or NgRX.

What is state management?

State management is a solution that is using events in order for different parts of the application to be aware of a change. There is a different approach also, where it’s possible to take a state in DOM or even a global object of the application. Store pattern is usually used where all mutable actions change the structure of the state. This type of state management facilitates a way of when and how a particular mutation can be triggered.

About Vuex best practices

Vuex should strictly be used for the following things:

  • When multiple parts of the application are expected to detect or change state 
  • When there is a need to share data between multiple components
  • When there is a complex interaction with the backend, for example multiple API calls
  • When an application interacts with a traditional REST API call or GraphQL

The basic things that Vuex contains are:

  • actions
  • mutations
  • state
  • getters

Let’s start with a simple application

We’ll develop a simple application that will allow users to monitor the weather forecast in seven Croatian cities. The weather data will be taken from free open OpenWeather API service (https://openweathermap.org/).

First, we’ll generate a project:
vue create vuex-open-weather

Then we’ll install Vuex in a project:
npm install –save vuex@next

After that, we need to install axios library:
npm install –save axios vue-axios

It is necessary to create a store folder that will contain two files:

  • store.js
  • service.js

Within the store.js, it’s necessary to initialize the Vuex store. Also we need to define empty objects for the following arguments:

  • state 
  • getters 
  • actions
  • mutations

Creating a store

import { createStore } from "vuex";
 
const state = {};
 
const getters = {};
 
const actions = {};
 
const mutations = {};
 
const store = createStore({
 state,
 mutations,
 actions,
 getters,
});
export default store;

Then, you need to initialize the store within main.js

import { createApp } from "vue";
import store from "./store/store";
import App from "./App.vue";
import router from "./router";
 
const app = createApp(App);
 
app.use(store).use(router);
app.mount("#app");

After it’s initialized, we’ll add two properties within the state object. State is an object that contains the state of the data at the level of the entire application. In practice, the data we store in state must correspond to the data in the component. All business logic must be done before saving data to the state. 

Our state will contain:

  • citiesWeather in which we’ll store the current forecast for specific cities
  • cityWeather where we’ll store a forecast for the next period for the selected city.
const state = {
 citiesWeather: {},
 cityWeather: {},
}


Before displaying data, it’s necessary to define the actions that will be called, together with service from which individual data will be called. In addition to actions, it’s also necessary to define mutations that will change the state. Each mutation is defined by type and a handler. Through the mutation, it’s possible to send a certain payload, which will eventually manipulate the data within the state.

The application contains two actions that refer to the service of the same name through which we retrieve data. Instead of directly manipulating data from a particular action, we commit individual mutations. With all that said it is a good practice that we can adjust the data in the way, we need it.

const actions = {
 async fetchWeatherForCities({ commit }) {
   const response = await service.fetchWeatherForCities();
   commit("addWeatherForCities", response);
 },
 async fetchWeatherForCity({ commit }, { cityId }) {
   const response = await service.fetchWeatherForCity(cityId);
   commit("addWeatherForCity", response);
 },
};
export async function fetchWeatherForCities() {
 try {
   const response = await axios.get(
     `${process.env.VUE_APP_URL}/group?id=${zagrebId},${osijekId},${rijekaId},${splitId},${dubrovnikId},${zadarId},${varazdinId}&appid=${process.env.VUE_APP_API_KEY}&units=metric`
   );
   return response.data;
 } catch (e) {
   console.error("Failed to fetch weather for cities", e);
 }
}
 
export async function fetchWeatherForCity(cityId) {
 try {
   const response = await axios.get(
     `${process.env.VUE_APP_URL}/forecast?id=${cityId}&appid=${process.env.VUE_APP_API_KEY}&units=metric`
   );
   return response.data;
 } catch (e) {
   console.error("Failed to fetch weather city", e);
 }
}


 
const mutations = {
 addWeatherForCities: (state, weather) => {
   state.citiesWeather = weather;
 },
 addWeatherForCity: (state, weather) => {
   state.cityWeather = weather;
 },
};

After the data has been successfully saved to the state, it’s necessary to define getters. Getters are used exclusively for communication between the state and the component.

const getters = {
 citiesWeather: (state) => {
   return state.citiesWeather.list;
 },
 cityWeather: (state) => {
   return state.cityWeather;
 },
};

If multiple components use the same getter, it’s best to define a global getter that will reduce boilerplate code. We can use an argument inside of the component to retrieve data of a certain getter. 

computed: {
   citiesWeather() {
     return this.$store.getters.citiesWeather;
   },
 },

Once the data is available for displaying inside of the component, we do a classic data binding between template and arguments or methods inside the component. Changing the data within the component as little as possible is recommended because as mentioned, the best practice is to change the data store in the mutation so that the state is the single source of truth.

Conclusion

The big advantage of Vuex is that there are no restrictions on how we structure the code. The most important thing, more precisely, the principle to adhere to is that everything is centralized within the store. We manipulate the data only during mutation. Having a global state management like Vuex is a benefit, especially when you develop a big application. The main benefits of developing and maintaining your application are much easier.

The whole code is available on my github repo – https://github.com/marmijic/vuex-open-weather

Next

Blog

Why and when to use dedicated teams in software development

Company

Our people really love it here

How it all started

Est. in 2014., gathering eight employees with eyes set on the future. No matter how set they were, they couldn’t predict the success and extent of growth that would ensue. Today there are more than 100 of us, and people are here to stay.

Stability in unstable times

The turmoil of 2020 caused great inconvenience for people all over the world. However, this did not affect our business. Quite the opposite — we not only kept all jobs and salaries intact, but we also grew in size. And we keep expanding. 

Contact

We’d love to hear from you