How to Use Vuex in a Nuxt Project demo repo
Vuex (opens new window) is a state management pattern and library for Vue.js applications.
This tutorial uses Github's Markdown API (opens new window).
# 👣 Steps
- Create a Nuxt project.
npx create-nuxt-app nuxt-demo
cd nuxt-demo
INFO
Vuex is already included in Nuxt so there is no need to install it.
- Create a template, e.g. in
pages/vuex.vue
.
<template>
<client-only>
<div>
<h1>Vuex Demo</h1>
<b-form-textarea
id="result"
v-model="input"
class="bg-dark container mb-4 text-white"
rows="10"
autofocus
@input="renderPreview"
>
</b-form-textarea>
<b-container
class="border border-primary"
v-html="renderedMd"
></b-container>
</div>
</client-only>
</template>
- Create a module, e.g.
store/markdown.js
.
export const 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
`,
renderedMd: ""
});
export const actions = {
async renderPreview({ commit }, input) {
try {
const url = `https://api.github.com/markdown`;
const response = await fetch(url, {
Accept: "application/vnd.github.v3+json",
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
}
}
};
export const mutations = {
updateInput(state, input) {
state.input = input;
},
renderedMd(state, result) {
state.renderedMd = result;
}
};
export const getters = {
input: state => {
return state.input;
},
renderedMd: state => {
return state.renderedMd;
}
};
- Use Vuex to complete the template, i.e. in
pages/vuex.vue
.
<template>
<client-only>
<div>
<h1>Vuex Demo</h1>
<b-form-textarea
id="result"
v-model="input"
class="bg-dark container mb-4 text-white"
rows="10"
autofocus
@input="renderPreview"
>
</b-form-textarea>
<!-- eslint-disable -->
<b-container
class="border border-primary"
v-html="renderedMd"
></b-container>
<!-- eslint-enable -->
</div>
</client-only>
</template>
<script>
export default {
computed: {
input: {
get() {
return this.$store.getters["markdown/input"];
},
set(value) {
this.$store.commit("markdown/updateInput", value);
}
},
renderedMd() {
return this.$store.getters["markdown/renderedMd"];
}
},
mounted() {
this.renderPreview();
},
methods: {
renderPreview() {
this.$store.dispatch("markdown/renderPreview", this.input);
}
}
};
</script>
# 📖 Explanation
Click if you are not familiar with Vuex yet 😕
In simple words, Vuex (opens new window) is something we can use to manage state in our Vue project. Store is the container. This store consists of 4 parts:
- state (opens new window)
- actions (opens new window)
- mutations (opens new window)
- getters (opens new window)
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. stringify
the input
for the body
here.
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 theinput
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.