지난 포스팅에서 알아본 Vuex로 Todo-App을 리팩토링 해보자.
Store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const storage = {
fetch() {
const arr = [];
if (localStorage.length > 0) {
for (let i = 0; i < localStorage.length; i++) {
if (localStorage.key(i) !== 'loglevel:webpack-dev-server') {
arr.push(JSON.parse(localStorage.getItem(localStorage.key(i))));
}
}
}
return arr;
}
}
export const store = new Vuex.Store({
state: {
todoItems: storage.fetch()
},
getters: {
getTodoItems(state) {
return state.todoItems;
}
},
mutations: {
addOneItem(state, todoItem) {
const obj = {completed: false, item: todoItem};
localStorage.setItem(todoItem, JSON.stringify(obj));
state.todoItems.push(obj);
},
removeOneItem(state, payload) {
state.todoItems.splice(payload.index, 1);
localStorage.removeItem(payload.todoItem.item);
},
toggleOneItem(state, payload) {
state.todoItems[payload.index].completed = !state.todoItems[payload.index].completed;
localStorage.removeItem(payload.todoItem.item);
localStorage.setItem(payload.todoItem.item, JSON.stringify(payload.todoItem));
},
clearAllItems(state) {
state.todoItems = [];
localStorage.clear();
}
}
});
TodoList.js
<template>
<section>
<transition-group name="list" tag="ul">
<li v-for="(todoItem, index) in this.storedTodoItems" class="shadow" v-bind:key="todoItem.item">
<i class="checkBtn fas fa-check" v-bind:class="{checkBtnCompleted: todoItem.completed}" v-on:click="toggleComplete(todoItem, index)"></i>
<span v-bind:class="{textCompleted: todoItem.completed}">{{ todoItem.item }}</span>
<span class="removeBtn" v-on:click="removeTodo(todoItem, index)">
<i class="removeBtn fas fa-trash-alt"></i>
</span>
</li>
</transition-group>
</section>
</template>
<script>
export default {
methods: {
removeTodo(todoItem, index) {
this.$store.commit('removeOneItem', {todoItem, index});
},
toggleComplete(todoItem, index) {
this.$store.commit('toggleOneItem', {todoItem, index});
}
},
computed: {
storedTodoItems() {
return this.$store.getters.getTodoItems;
}
}
}
</script>
TodoInput.js
<template>
<div class="inputBox shadow">
<input type="text" v-model="newTodoItem" @keyup.enter="addTodo">
<span class="addContainer" v-on:click="addTodo">
<i class="addBtn fas fa-plus" aria-hidden="true"></i>
</span>
<Modal v-if="showModal" @close="showModal = false">
<h3 slot="header">
경고
<i class="closeModalBtn fa fa-times"
aria-hidden="true"
@click="showModal = false">
</i>
</h3>
<p slot="body">할 일을 입력하세요.</p>
</Modal>
</div>
</template>
<script>
import Modal from './common/Modal.vue'
export default {
data() {
return {
newTodoItem: '',
showModal: false
}
},
methods: {
addTodo() {
if (this.newTodoItem !== '') {
const item = this.newTodoItem.trim();
this.$store.commit('addOneItem', item);
this.clearInput();
} else {
this.showModal = !this.showModal;
}
},
clearInput() {
this.newTodoItem = '';
}
},
components: {
Modal
}
}
</script>
TodoList.js
<template>
<section>
<ul>
<li v-for="(todoItem, index) in propsdata" class="shadow" v-bind:key="todoItem.item">
<i class="checkBtn fas fa-check" v-bind:class="{checkBtnCompleted: todoItem.completed}" v-on:click="toggleComplete(todoItem, index)"></i>
<span v-bind:class="{textCompleted: todoItem.completed}">{{ todoItem.item }}</span>
<span class="removeBtn" v-on:click="removeTodo(todoItem, index)">
<i class="removeBtn fas fa-trash-alt"></i>
</span>
</li>
</ul>
</section>
</template>
<script>
export default {
props: ['propsdata'],
methods: {
removeTodo: function(todoItem, index) {
this.$store.commit('removeOneItem', {todoItem, index});
},
toggleComplete: function(todoItem, index) {
this.$store.commit('toggleOneItem', {todoItem, index});
}
}
}
</script>
TodoFooter.js
<template>
<div class="clearAllContainer">
<span class="clearAllBtn" v-on:click="clearTodo">Clear All</span>
</div>
</template>
<script>
export default {
methods: {
clearTodo() {
this.$store.commit('clearAllItems');
}
}
}
</script>
이전에 this.$emit으로 App.vue라는 상위 컴포넌트로 모두 처리를 이관했다면, 지금은 store.js라는 Vuex로 처리를 각 컴포넌트에서 이관한다.
store에서는 각 로직을 처리 후 list를 갱신하여 TodoList.vue에 즉시 바인딩하며 리스트를 갱신한다.
다음 포스팅에서는 vuex를 좀 더 효율적으로 사용하는 방법에 대해 알아보도록 하자.