Dana Janoskova
Senior Software Engineer at
Vue.js, React, Node.js, CSS, Flutter
Vue CLI has support for vuex out of the box.
npm install -g @vue/cli
# OR
yarn global add @vue/cli
This is how you call a mutation in an action. The first argument is the mutation name and the second is the data you wish to pass in.
Import mapActions, mapGetters and mapMutations from 'vuex' to use them in your components.
In this example, the mounted() method calls POSTS_FETCH(), which after being executed and having received data, updates the vuex state.
Then our computed variable from getter will trigger a re-render of the component.
PostsContainer.vue ← connected to vuex
You'd then access and render the posts the same way as you access your internal state.
Post.vue ← an independent presentational component,
not connected to vuex
Let's start simple 😁
state: {
posts: []
}
mutations: {
POSTS_SET(state, data) {
state.posts = data
},
POST_ADD(state, data) {
state.posts.unshift(data)
},
POST_MODIFY(state, data) {
const postIndex = state.posts.findIndex(p => p.id === data.id)
state.posts[postIndex] = data
},
POST_REMOVE(state, id) {
state.posts = state.posts.filter(p => p.id !== id)
}
}
actions: {
async POSTS_FETCH(context) {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts')
const posts = response.data.slice(0, 20)
context.commit('POSTS_SET', posts)
return posts
},
async POST_CREATE(context, data) {
const response = await axios.post('https://jsonplaceholder.typicode.com/posts', data)
const post = response.data
context.commit('POST_ADD', post)
return post
},
async POST_UPDATE(context, data) {
const response = await axios.put('https://jsonplaceholder.typicode.com/posts/' + data.id, data)
const post = response.data
context.commit('POST_MODIFY', post)
return post
},
async POST_DELETE(context, id) {
await axios.delete('https://jsonplaceholder.typicode.com/posts/' + id)
context.commit('POST_REMOVE', id)
}
}
getters: {
posts(state) {
return state.posts
},
postById: (state) => (id) => {
return state.posts.find(post => post.id === id)
}
}
<template>
<div>
<Post v-for="post in posts" :key="post.id" :post="post" />
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import Post from '../components/Post'
export default {
mounted() {
if (!this.posts.length) this.POSTS_FETCH()
},
methods: {
...mapActions(['POSTS_FETCH'])
},
computed: {
...mapGetters(['posts'])
},
components: { Post }
}
</script>
<template>
<div class="post-container">
<h3>{{ post.title }}</h3>
<div class="post-body">
{{ post.body }}
</div>
<div class="post-actions">
<router-link :to="{ name: 'edit', params: { id: post.id } }">Edit</router-link>
|
<a @click="handleDelete">Delete</a>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
props: {
post: Object
},
methods: {
...mapActions(['POST_DELETE']),
handleDelete () {
this.POST_DELETE(this.post.id)
}
}
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<input type="text" placeholder="Post title" v-model="model.title" />
<br />
<textarea placeholder="Post body" v-model="model.body" rows="5" />
<button type="submit">
<template v-if="isEdit">Save</template>
<template v-else>Create</template>
</button>
</form>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
data() {
return {
model: {
title: '',
body: ''
}
}
},
methods: {
...mapActions([
'POSTS_FETCH',
'POST_CREATE',
'POST_UPDATE'
]),
async handleInputsFill() {
let posts = this.posts
if (!posts.length) {
posts = await this.POSTS_FETCH()
}
const { id } = this.$route.params
if (!id) {
this.model = {
title: '',
body: ''
}
return
}
const result = posts.find(post => post.id === Number(id))
this.model = { ...result }
},
async handleSubmit () {
const promise = this.isEdit ? this.POST_UPDATE : this.POST_CREATE
await promise(this.model)
this.$router.push({ name: 'posts' })
}
},
computed: {
...mapGetters([
'posts'
]),
isEdit () {
return !!this.$route.params.id
}
},
mounted() {
this.handleInputsFill()
},
watch: {
'$route'() {
this.handleInputsFill()
}
}
}
</script>