TIL | Vue 09일차 (할 일 추가, 삭제, 완료기능 코드리팩토링)

space's pace·2022년 10월 8일
0

Vue

목록 보기
8/9

할 일 추가 리팩토링

App.vue

<template>
  <div id="app">
    <Todo-header></Todo-header>
    <todo-input v-on:addTodoItem="addOneItem"></todo-input>
    <todo-list
        v-bind:propsdata="todoItems"
    ></todo-list>
    <todo-footer></todo-footer>
  </div>
</template>

v-on:하위 컴포넌트에서 발생시킨 이벤트 이름="현재 컴포넌트의 메서드명"
v-on:하위 컴포넌트에서 발생시킨 이벤트 이름="addOneItem"

App.vue

<script>
export default {
  data: function () {
    return {
      todoItems: []
    }
  },
  methods: {
    // 각각의 컴포넌트에서는 표현만 하고, 실질적인 데이터 처리는 App.vue 에서 하고 있다.
    addOneItem: function (todoItem) {
      // 저장하는 로직
      const obj = {
        completed: false,
        item: todoItem,
        time: getDate().time
      }
      localStorage.setItem(todoItem, JSON.stringify(obj))
      // console.log(localStorage.setItem(todoItem, JSON.stringify(obj)))
      this.todoItems.push(obj)
    }
  // 인스턴스가 생성되자마자 호출되는 로직
  created: function () {
    if (localStorage.length > 0) {
      
      for (let i = 0; i < localStorage.length; i++) {
        if (localStorage.key(i) !== 'loglevel:webpack-dev-server') {
          this.todoItems.push(JSON.parse(localStorage.getItem(localStorage.key(i))))
        }
      }
    }
  },
  components: {
    'TodoHeader': TodoHeader,
    'TodoInput': TodoInput,
    'TodoList': TodoList,
    'TodoFooter': TodoFooter
  }
}
</script>

TodoInput.vue

<script>
export default {
  data: function () {
    return {
      newTodoItem: '',
    }
  },
  methods: {
    addTodo: function () {
      if (this.newTodoItem !== '') {
        this.$emit('addTodoItem', this.newTodoItem)
        this.clearInput()
      } 
    },
    clearInput: function () {
      this.newTodoItem = ''
    }
  }
}
</script>

this.emit(이밴트이,인자1,인자2...)emit('이밴트이름', 인자1, 인자2...)emit (자식에서 부모로 데이터를 줄 때)

  • 정리하자면,
    todoInput 컴포넌트 (자식) |
    addTodo 버튼을 클릭하거나, 엔터를 누르면 그러면 addTodo가 동작한다.
    그러면 this.$emit 에서 addTodoItem 이 발동하고, this.newTodoItem 텍스트 값이 같이 올라간다 (부모 컴포넌트로)

App 컴포넌트 (부모) |
emit으로 올려보낸 addTodoItem(하위컴포넌트 이벤트명) 로 신호를 보내고, addOneItem(현재 컴포넌트 이벤트명) 이 실행된다.

addOneItem: function (todoItem) {
      const obj = {
        completed: false,
        item: todoItem
      }
      localStorage.setItem(todoItem, JSON.stringify(obj))
  	  // 로컬스토리지의 데이터 목록과 화면의 할 일 목록의 동기화
      this.todoItems.push(obj)

하위컴포넌트에서 인자를 보냈기 때문에 인자의 값을 todoItem 로 써서 this.newTodoItem를 맵핑시켜준다.

할 일 삭제 리팩토링

App.vue

<template>
<div id="app">
  <Todo-header></Todo-header>
  <todo-input v-on:addTodoItem="addOneItem"></todo-input>
  <todo-list
      v-bind:propsdata="todoItems"
      v-on:removeItem="removeOneitem"
  ></todo-list>
  <todo-footer></todo-footer>
</div>
</template>

App.vue

<script>
/..

export default {
  // 로컬스토리지에서 꺼내 담을 데이터 선언
  data: function () {
    return {
      // 모든 컴포넌트에서 동일하게 쓰는 데이터
      todoItems: []
    }
  },
  methods: {
    // 각각의 컴포넌트에서는 표현만 하고, 실질적인 데이터 처리는 App.vue 에서 하고 있다.
    addOneItem: function (todoItem) {
      // 저장하는 로직
      const obj = {
        completed: false,
        item: todoItem,
        time: getDate().time
      }
      localStorage.setItem(todoItem, JSON.stringify(obj))
      this.todoItems.push(obj)
    },
    // 인자도 똑같이 넘겨주면 된다.
    removeOneitem: function (todoItem, index) {
      // 로컬스토리지 - removeItem(key) : 로컬스토리지의 아이템을 삭제해주는 거 (괄호안에는 key값을 넣는다)
      localStorage.removeItem(todoItem.item)
      // 화면에서 리스트를 삭제해주는 거 - todoItems 배열의 splice 함수를 이용해 삭제 (배열변경을 시작할 index, 삭제할 갯수)
      this.todoItems.splice(index, 1)
    },
  ../
}
</script>

TodoList.vue

<script>
export default {
  props: ['propsdata'],
  methods: {
    removeTodo: function (todoItem, index) {
      this.$emit('removeItem', todoItem, index)
      // localStorage.removeItem(todoItem)
      // this.todoItems.splice(index, 1)
      // 이 로직을 App컴포넌트로 전달
    }
  }
}
</script>

addOneItem처럼 emit으로 자식 컴포넌트에서 부모 컴포넌트로 이벤트 전달 (인자도 똑같이 넘겨준다)

removeItem에 todoItem 을 콘솔로그로 찍으면 object 형태로 찍힌다. todoItem을 그대로 넘겼는데 App.vue에서 객체로 지워버리면 정상적으로 지워지지 않는다. key랑 todoItem의 item의 값이 동일하기 때문에 지울 수 있다.

할 일 완료 리팩토링

App.vue

<script>
/...

export default {
  // 로컬스토리지에서 꺼내 담을 데이터 선언
  data: function () {
    return {
      // 모든 컴포넌트에서 동일하게 쓰는 데이터
      todoItems: []
    }
  },
  methods: {
    /..
    toggleOneItem: (todoItem, index) => {
      // todoItem.completed = !todoItem.completed => 안티패턴
      // 이유 : todoItems 라는 배열을 propsdata 로 TodoList.vue 에 내려주고, 이벤트버스로 todoItem 을 다시 넘겨줌 (즉, props 로 접근된 데이터를 다시 위로 끌어올린 것)
      // 해결 방법 : 이벤트버스로 데이터를 끌어올려 변경하는 게 아닌 데이터에 직접 접근하여 변경하는 게 나음
      // 대체 코드 (컴포넌트의 경계를 명확하게 한 코드)
      this.todoItems[index].completed = !this.todoItems[index].completed
      // 로컬 스토리지의 데이터를 갱신
      localStorage.removeItem(todoItem.item)
      localStorage.setItem(todoItem.item, JSON.stringify(todoItem))
    }
  } 
}
</script>

TodoList.vue

<template>
  <div>
    <ul>
      <li v-for="(todoItem, index) in propsdata" v-bind:key="todoItem.item" class="shadow">
        <i class="checkBtn fa-solid 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>
        ../
</template>

TodoList.vue

<script>
export default {
  props: ['propsdata'],
  methods: {
    /..
    toggleComplete: function (todoItem, index) {
      this.$emit('toggleItem', todoItem, index)
    }
  }
}
</script>
profile
블로그 이사 준비중!

0개의 댓글