How to Use Vuex in a Vue Project demo repo

Vuex is a state management pattern and library for Vue.js applications.

This tutorial uses Github's Markdown API.

👣 Steps

  1. Create a Vue project and select the Vuex feature. If it is inside an existing project, add Vuex with vue add vuex.
npm i -g @vue/cli
vue create vue-demo
  1. Register a new GitHub App to get a client ID and a client secret.

  2. Create a file named .env in the project root and paste your client ID and client secret. For example:

VUE_APP_MARKDOWN_CLIENT_ID=a1b2c3d4e5f6g7h8i9j0
VUE_APP_MARKDOWN_CLIENT_SECRET=a1b2c3d4e5f6g7h8i9j0a1b2c3d4e5f6g7h8i9j0
  1. Register dotENV and Vuex in src/main.js.
require("dotenv").config();

import store from "./store/";

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app");

INFO

The next steps are not strictly in order. Expect to jump around working on those files at the same time.

  1. Create a template, e.g. in src/views/vuex.vue.
<template>
  <div>
    <h1>Vuex Demo</h1>

    <textarea
      id="result"
      v-model="input"
      class="bg-dark container mb-4 text-white"
      rows="10"
      autofocus
      @input="renderPreview"
    >
    </textarea>

    <div class="border border-primary container" v-html="renderedMd"></div>
  </div>
</template>
  1. Create a module, e.g. src/store/markdown.js.
export default {
  state: {
    input: `# Under the Sea

  *Source*: [Color of the year **2019**](https://www.pantone.com/color-intelligence/color-of-the-year/color-of-the-year-2019-palette-exploration)

  > Awash in color suggestive of the watery environment that lies beneath a tropical island paradise, Under The Sea places \`PANTONE\` Living Coral at the center of our naturally vivid and chromatic ecosystem, evocative of how coral reefs embrace with their warmth and nourishment and provide shelter to a diverse kaleidoscope of colorful sea life.

  ---

  * Sea pink
  * Limpet Shell
  * Living Coral
  * Vibrant Yellow
  * Turkish Sea
  * Turtle Green
  * Blue Depths

  ![Color harmonies](/color-harmonies.png)`,
    renderedMd: ""
  },

  actions: {
    async renderPreview({ commit }, input) {
      try {
        const url = `https://api.github.com/markdown?client_id=${
          process.env.VUE_APP_MARKDOWN_CLIENT_ID
        }&client_secret=${process.env.VUE_APP_MARKDOWN_CLIENT_SECRET}`;

        const response = await fetch(url, {
          method: "POST",
          headers: {
            "Content-Type": "text/html"
          },
          body: JSON.stringify({ text: input, mode: "gfm" })
        });

        if (response.ok) {
          const renderedMd = await response.text();

          commit("renderedMd", renderedMd);
        }
      } catch (error) {
        console.log(error); // eslint-disable-line no-console
      }
    }
  },

  mutations: {
    updateInput(state, input) {
      state.input = input;
    },
    renderedMd(state, result) {
      state.renderedMd = result;
    }
  },

  getters: {
    input(state) {
      return state.input;
    },
    renderedMd(state) {
      return state.renderedMd;
    }
  }
};
  1. Register the module in src/store/index.js:
import Vue from "vue";
import Vuex from "vuex";

import markdown from "./markdown";

Vue.use(Vuex);

export default new Vuex.Store({
  namespaced: true,
  modules: {
    markdown
  }
});
  1. Use Vuex to complete the template, i.e. in src/views/vuex.vue.
<template>
  <div>
    <h1>Vuex Demo</h1>

    <textarea
      id="result"
      v-model="input"
      class="bg-dark container mb-4 text-white"
      rows="10"
      autofocus
      @input="renderPreview"
    >
    </textarea>

    <div class="border border-primary container" v-html="renderedMd"></div>
  </div>
</template>

<script>
export default {
  computed: {
    input: {
      get() {
        return this.$store.getters.input;
      },
      set(value) {
        this.$store.commit("updateInput", value);
      }
    },
    renderedMd() {
      return this.$store.getters.renderedMd;
    }
  },
  mounted() {
    this.renderPreview();
  },
  methods: {
    renderPreview() {
      this.$store.dispatch("renderPreview", this.input);
    }
  }
};
</script>

📖 Explanation

Click if you are not familiar with Vuex yet 😕

In simple words, Vuex is something we can use to manage state in our Vue project. Store is the container. This store consists of 4 parts:

Our Vue component render the initial state. Then, when we want to change it, perhaps after a button click, the component calls an action with dispatch. Vuex actions contains the actions we can use.

The called action does something, for example, make an API call. That action results in something we can change the state with. So at the end of the action, we commit that result. Vuex mutations contains the changes or mutations we can make.

When the change is already committed, the mutations mutate the state. Our component can access the state using getters. Getters are like the final processor of your state, so your component can get a ready to use state.

The component can also access the state without getters. It is OK to omit the getter if there is no need to process the state.

Finally, the component will re-render. That is it.

Template

@input="renderPreview" means that when there is a change, the renderPreview method will be executed.

The rendered Markdown will be placed inside div under the textarea.

The textarea is bound to the input in the computed part. When there is an input, the set part will commit the changes. The get part is what the input will show.

Some Markdown input is already present as an example, but the method will not be called without any input change. Because of that, in the mounted part the method is called.

Finally, the methods contain the renderPreview method. This dispatches the renderPreview action, bringing the input to be passed.

Store

The input has the example mentioned earlier. When there is a change, the renderPreview action is called. URL and body is prepared here.

Pass the client ID and client secret to the URL with process.env.. stringify the input for the body.

renderedMd is taken from the response. commit that with commit("renderedMd", renderedMd);. The first renderedMd corresponds to the item in mutations, while the second one is the result to be passed.

There are 2 items in mutations:

  • updateInput to change the input state.
  • renderedMd to update the result.

getters return the state.

INFO

The getters here only return the state without any change, so you can actually get the state without getters in the component. It is my default to use a getter so that when I need to edit the state, I will not need to make lots of changes.

Last updated: 6/21/2019, 2:32:45 AM