Todo-App Vuex로 리팩토링

devjune·2021년 6월 24일
0

Vue.js

목록 보기
12/36

지난 포스팅에서 알아본 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를 좀 더 효율적으로 사용하는 방법에 대해 알아보도록 하자.

출처 인프런 Vue.js 중급 강좌 - 웹앱 제작으로 배워보는 Vue.js, ES6, Vuex

profile
개발자준

0개의 댓글