DEV Community

Cover image for Error Handling in Vue with Vuex
Tyler V. (he/him)
Tyler V. (he/him)

Posted on • Edited on • Originally published at terabytetiger.com

Error Handling in Vue with Vuex

When building an application for your users, a common piece of functionality to include is a way to tell the user something went wrong, and preferably in a way they can determine what happened (or perhaps more accurately - what didn't happen).

For a recent work project, I decided to try and handle errors with Vuex in a way inspired by flash messages. I was happy with the outcome and thought I'd share! Enjoy! 🌮

Prerequisites 📚

I'm going to be writing with the assumption that you have a base understanding of Vue's Single File Components and Vuex. If you want a detailed intro to Vuex, I think this post covers things nicely:

Setup 🔨

There are 3 files that we need to make sure are setup to handle:

  1. Storing our error (State files)
  2. Displaying our error (Vue Components/Layouts)

In our store, we want to setup the following state which will allow us to log our error so that it can be displayed throughout the application:

// store/index.js

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    error: ""
  }
});

Enter fullscreen mode Exit fullscreen mode

Next, we'll need to create a small component to display the error. I created my error message as a "toast" style with a fade-in and fade-out transition with a button to dismiss the error once the user is finished with it.

<!-- /src/components/ErrorMessage.vue -->
<template>
  <transition name="fadeAway">
    <div
      class="card errorMessage"
      v-if="this.$store.state.error"
    >
      <h2 class="card-title">
        Error
      </h2>
      <p class="p-left p-right">
        {{ this.$store.state.error }}
      </p>
      <div class="card-actions">
        <button
          class="dismiss btn-red"
          @click.prevent="dismiss"
          aria-label="Dismiss Error"
        >
          Dismiss Error
        </button>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  name: "ErrorMessage",
  methods: {
    dismiss() {
      this.$store.state.error = "";
    }
  }
};
</script>

<style scoped>
/* Not the cleanest CSS I've ever written - trying to loop my extra helper classes into as minimal css as possible 🍻*/

.card {
  background: hsl(0, 80%, 95%);
  margin-top: 15px;
  margin-left: auto; 
  margin-right: auto; 
  border-radius: 7px; 
  padding: 0 0 5px;
  text-align: center;
}

.card-title {
  text-align: left;
  background-color: hsl(0, 80%, 70%);
  margin: 0;
  border-radius: 7px 7px 0 0;
  color: #f3f3f3;
  padding: 5px 10px;
  font-size: 1.17em;
}

.card-actions {
  text-align: right; 
  padding-right: 1rem;
  padding-bottom: .5rem;
}

.card-actions > button {
  padding: .2rem 1rem;
}

.btn-red {
  background-color: #eb4747;
  border-color: #eb4747;
  color: #f3f3f3;
  border-radius: 4px;
}

.fadeAway-enter-active {
  transition: all 0.3s ease;
}

.fadeAway-leave-active {
  transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}

.fadeAway-leave-to,
.fadeAway-enter {
  opacity: 0;
  transform: translateX(100px);
}

.errorMessage {
  position: fixed;
  right: 50px;
  bottom: 50px;
  max-width: 400px;
  min-width: 200px;
}
</style>
Enter fullscreen mode Exit fullscreen mode

In the event of an error, the user should see something somewhat like this (font import not included above):
A material card with red-salmon coloring. A Darker header background says

Finally, the last bit of setup work to do is to include your Error Message component in your App.vue file, which should look something like this:

<!-- src/App.vue -->
<template>
  <div id="app">
    <ErrorMessage />
    <router-view />
  </div>
</template>

<script>
import ErrorMessage from "@/components/Layout/ErrorMessage.vue";

export default {
  components: { ErrorMessage }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Setting an Error message 🚨

Now we've got our application setup to display and store the error - so how do we set the error?

Within a component:

When you're within a component's lifecycle hooks or methods, you can set the value of our error with this.$store.state.error = "Your error message here".

For example, I have a piece that checks if the user has an email on their profile - and throws an error if they don't - which looks like this:

if (this.$auth.profile.email) {
  // ...
} else {
  this.$store.state.error = "Sorry, your account doesn't have an email associated with it.";
}
Enter fullscreen mode Exit fullscreen mode

Within State:

If you'd prefer to create actions and mutations for your state, you can update your state from above to look like this:


export default new Vuex.Store({
  state: {
    error: ""
  },
  mutations: {
    POST_ERROR: (state, payload) => {
      state.error = payload;
    }
  },
  actions: {
    SET_ERROR: (context, errorMsg) => {
      context.commit("POST_ERROR", errorMsg)
    }
  }
});

Enter fullscreen mode Exit fullscreen mode

then you can use mapActions in your component to call your action:

// Component.vue
{
// ...
methods: {
  ...mapActions(["SET_ERROR"]),
  checkEmail() {
    if(userHasEmail) {
      //...
    } else {
      this.SET_ERROR("Sorry, your account doesn't have an email associated with it.")
    }
  }
}
// ...
}
Enter fullscreen mode Exit fullscreen mode

From a store module:

In the process of code splitting my store into modules, I realized I would need a way to set the state's error value from the module in the event of an error. Vue has a great way to handle this in that any action function's context argument contains the ability to access parent state.

Within any module's action, you can reference parent state with rootState such as:

context.rootState.error = "There was an error! 🚨🚨🚨" 
Enter fullscreen mode Exit fullscreen mode

Where to go from here 🚀

While this is one way to implement this, something you may have noticed is that this will only ever display a single error. It also requires the user to dismiss it, which may not be appropriate depending on your application.

If you're intrigued by this implementation, I'd encourage you to start tweaking it to fit your needs!

Top comments (2)

Collapse
 
javascriptmick profile image
Michael Dausmann

Would be interested to see how you would handle errors from a rest layer e.g. fetch or axios. I usually do this by handling promise errors in the action and committing an error mutation.

For bonus points, how would you centrally handle things like auth timeouts on rest calls where you might like to do something common, like prompt for login?

Collapse
 
mihalidis profile image
Pınar Suvaçoğlu

I want to thank you.
the description and the code are very clear. as an intern, I understand this very easy for this. Thank you!!