본문 바로가기
[WEB]/React

React - To Do Item 삭제

by hi_kmin6 2020. 3. 12.

오늘은 지난 글에 이어서 To Do Item을 삭제하는 기능을 추가해보았습니다.

 

일단 지난 글에서 추가한 부분에 오타가 있었습니다.

오타 수정 및 삭제하는 기능을 넣은 코드 먼저 보이고 그다음 시행착오들을 적어보겠습니다.

 

 


# 오늘의 작업

ToDoTemplate.js

import React from "react";
import ToDoItem from "./ToDoItem";

class ToDoTemplate extends React.Component {
  toDoList = [];

  constructor(props) {
    super(props);
    this.toDoList = JSON.parse(localStorage.getItem("toDoList")) || [];
    // localStorage에서 toDoList라는 이름의 데이터를 가져오되, 존재하지 않을 시에는 빈 배열을 반환 합니다.
    this.state = {
      todoItems: this.toDoList
    };
  }

  // List의 Items를 지우거나 추가할 때 LocalStorage와 State를 변경해주기위한 함수
  saveToDos = () => {
    localStorage.setItem("toDoList", JSON.stringify(this.toDoList));
    this.setState({ todoItems: this.toDoList });
  };

  // form에서 toDoItem을 입력 후 제출하는 이벤트에 대한 함수
  itemSubmit = event => {
    const todoTemplate = document.querySelector(".template-Container"),
      todoForm = todoTemplate.querySelector(".template-Form"),
      todoInput = todoForm.querySelector("input");

    const date = new Date(),
      dateCode =
        date.getFullYear() + "." + (date.getMonth() + 1) + "." + date.getDate();
    const newID =
      date.getHours() + "" + date.getMinutes() + "" + date.getSeconds();

    const toDoObj = {
      id: newID,
      text: todoInput.value,
      checked: false,
      writeDate: dateCode
    };

    this.toDoList.push(toDoObj);

    this.saveToDos();

    event.preventDefault();
    todoInput.value = "";
  };

  itemRemove = id => {
    const cleanToDoList = this.toDoList.filter(function(toDo) {
      return toDo.id !== id;
    });
    this.toDoList = cleanToDoList;
    this.saveToDos();
  };

  itemCheck = id => {};

  // render가 진행된 뒤에 eventListner를 추가
  componentDidMount() {
    const todoTemplate = document.querySelector(".template-Container"),
      todoForm = todoTemplate.querySelector(".template-Form");

    todoForm.addEventListener("submit", this.itemSubmit);
  }

  render() {
    const { todoItems } = this.state;

    return (
      <div className="template-Container">
        <form className="template-Form">
          <input type="text" placeholder="오늘의 할일" />
        </form>
        <div className="template-list">
          {todoItems.map(item => {
            return (
              <ToDoItem
                id={item.id}
                text={item.text}
                checked={item.checked}
                writeDate={item.writeDate}
                itemRemove={this.itemRemove}
              />
            );
          })}
        </div>
      </div>
    );
  }
}

export default ToDoTemplate;

 

코드 중간 부분에 "itemRemove = (id) => {}"가 할 일 항목을 삭제하는 함 수입니다.

ToDoItem에 id값을 넘겨주기 때문에 itemRemove를 호출하는 Component의 id를 받아와,

filter함수를 이용하여 해당 id 이외의 것들만을 남긴 배열을 새로 만듭니다.

그리고 새로운 배열을 다시 클래스 필드 toDoList에 넣고, State도 변경해줍니다.

 

그리고 ToDoItem Component에서 ToDoTemplate의 함수를 이용하기 위해서,

itemRemove 함수를 props로 넘겨주었습니다.

 

 

ToDoItem.js

import React from "react";

function ToDoItem({ id, text, checked, writeDate, itemRemove }) {
  return (
    <div className="item-container">
      <div className="item-remove" onClick={() => itemRemove(id)}>
        ✖
      </div>
      <div className="item-text">{text}</div>
      <div className="item-check">✔</div>
    </div>
  );
}

export default ToDoItem;

'✖'가 속한 div 태그에 onCick 속성에 props로 가져온 itemRemove함수를 넣어 주었습니다.

따라서 ✖를 누르게 되면, ToDoTemplate의 itemRemove가 id값을 받아서 해당 항목을 지우게 됩니다.

이에 따라 State가 변경되어, render함수가 다시 호출되어 화면에서도 해당 항목이 없는 채 표시됩니다.

 

 


# 오류 수정

 

# 오류 1

문제점 : 삭제 후 화면이 이전 상태를 유지

원인 : State 값 오타 (아직 내부 동작의 오류는 파악은 되지 않았습니다)

해결책 : 오타 없도록 변수명 검사 잘할 것.

 

ToDoTemplate.js(지난 글에 있던 코드 일부)

import React from "react";
import ToDoItem from "./ToDoItem";

class ToDoTemplate extends React.Component {
  toDoList = [];

  constructor(props) {
    super(props);
    this.toDoList = JSON.parse(localStorage.getItem("toDoList")) || [];
    // localStorage에서 toDoList라는 이름의 데이터를 가져오되, 존재하지 않을 시에는 빈 배열을 반환 합니다.
    this.state = {
      todoItems: this.toDoList
    };
  }

  // List의 Items를 지우거나 추가할 때 LocalStorage와 State를 변경해주기위한 함수
  saveToDos = () => {
    localStorage.setItem("toDoList", JSON.stringify(this.toDoList));
    this.setState({ ToDoItems: this.toDoList });
  };
  
...

}

export default ToDoTemplate;

 

여기서 saveToDos()를 보시면,

제가 생성자를 통해 만든 State와 saveToDos()를 변경하는 State의 이름이 각각 todoItems와 ToDoItems로 달랐습니다.

이를 todoItems로 동일하게 수정했습니다.

 

 

이전에 작성한 글에도 고쳐 두었습니다.

 

 

저 오타로 인한 문제는,

첫째로 아이템을 추가하면 State에 todoItems와 ToDoItems라는 두 가지 값이 생겨 모두 업데이트됩니다.

 

두 번째로 아이템을 지우는 기능을 추가했을 때, localStorage에서는 해당 값이 지워졌지만, 화면 상에서는 아직 보인다는 점이었습니다.

 

아직 오타가 남아있는 상태에서 render함수에 로그를 찍도록 해봤습니다.

 

 render() {
    const { todoItems } = this.state;
    console.log(this.state);
    
    ...
    
}

 

 

React App을 실행시켜 보았을 때 다음과 같습니다.

 

 

여기서 Work1이라는 할 일을 하나 추가해보았습니다.

 

 

오타 존재 초기 화면

 

 

일단 처음 화면에선 State에 todoItems 밖에 존재하지 않습니다.

여기서 Work1이라는 할일을 추가해보겠습니다.

 

 

Work1 추가 뒤 화면

 

 

아래에 빨간 부분은 제가 ToDoItem에 key value를 넣어주지 않아서 그런 것이니 신경 쓰실 필요는 없습니다.

list형식으로 된 것에는 key값이 필요하다고 합니다. 추후 추가하겠습니다.

 

Work1을 입력하고 나서 보시면, todoItems와 ToDoItems 두 개의 배열이 생성된 것을 볼 수 있습니다.

 

 

신기한 것은 제가 saveToDos()에 State를 변경시킨 것은 ToDoItems하나뿐인데, 기존의 State에 있던 todoItems 또한 내용이 변경된 것입니다.

 

일단 추가적으로 삭제를 진행해보겠습니다.

 

 

Work1삭제 진행

 

 

위의 사진의 마지막 log를 보시면 todoItems는 아직 Work1 데이터가 남아있습니다.

그래서 화면에서도 사라지지 않았습니다.

 

하지만, ToDoItem에서 Work1은 사라졌습니다.

 

 

이 원인을 찾기 위해서 또 다른 시도를 해봤습니다.

 

<방법 1>

  1. Work1 ~ 3 추가

  2. Work1 삭제

  3. Work4 & 5 추가

  4. Work3 삭제

 

1을 진행하는 동안은 todoItems와 ToDoItems 둘 다 추가가 일어났습니다.

하지만 2를 진행하면 ToDoItems에서만 삭제가 발생했고, 이후 3,4를 진행해도 todoItems에서는 변화가 없고 ToDoItems만 변화가 생겼습니다.

 

이것의 원인이 무엇인지 계속 찾아봤지만 알 수 없어 Component의 생명주기 중 todoItems를 건드리는 것은 Constructor가 유일하기에 방법 2를 수행해 보았습니다.

 

<방법 2>

1. Constructor, componentDidMount, componentDidUpdate, componentWillUnmount를 넣고 log를 찍도록 코드를 삽입

 

위 방법을 진행하여 방법 1에서 1번의 결과가,

삭제를 진행하기 전까지 ToDoItem Component에서 행위가 일어나지 않기 때문에 변화의 주체가 ToDoTemplate이기 때문에 Constructor가 혹시 다시 실행되는 것이 아닐까 하는 의구심이 들었습니다.

 

하지만 찍혀있는 log를 봤을 땐 Constructor는 단 한 번만 찍혀있었고 원인을 알 수 없었습니다.

 

추후 계속 원인을 찾아보고자 합니다.

원인은 발견하지 못했지만, 절대로 사용하고자 하는 것에 대하여 오타가 없어야 한다는 방지책을 얻었습니다.

 

 

# 오류 2

문제점 : Component Update가 무한 호출

원인 : onClick에 대한 함수를 event가 일어난 경우 호출해야 하지만, 지정 시 호출

해결책 : 즉시 호출이 아닌 다른 방법 사용.

 

ToDoItem.js

import React from "react";

function ToDoItem({ id, text, checked, writeDate, itemRemove }) {
  return (
    <div className="item-container">
      <div className="item-remove" onClick={itemRemove(id)}>
        ✖
      </div>
      <div className="item-text">{text}</div>
      <div className="item-check">✔</div>
    </div>
  );
}

export default ToDoItem;

 

 

위의 코드는 ✖가 속한 div 태그 안에 onClick의 값으로 {itemRemove(id)}를 넣어 주어 즉시 호출하는 상태가 되었습니다. 

그 결과 Component가 생성되었을 때 계속해서 setState를 호출하게 되기 때문에 update가 많이 호출돼 다음과 같은 Erorr가 발생합니다.

 

 

update 오류 발생

 

이를 해결하기 위해선 ToDoTemplate에서 했듯 addEventListner를 사용하는 방법이 있습니다.

하지만 addEventListner를 사용하기엔 HTML이 이미 화면상에 존재해야 했기 때문에 저는 다른 방법으로 글의 도입부에 있는 방법으로 해결했습니다.

 

import React from "react";

function ToDoItem({ id, text, checked, writeDate, itemRemove }) {
  return (
    <div className="item-container">
      <div className="item-remove" onClick={() => itemRemove(id)}>
        ✖
      </div>
      <div className="item-text">{text}</div>
      <div className="item-check">✔</div>
    </div>
  );
}

export default ToDoItem;

 

위 방법은 아래의 방법과 같습니다.

 

import React from "react";

function ToDoItem({ id, text, checked, writeDate, itemRemove }) {
  const remove = () => {
    itemRemove(id);
  };

  return (
    <div className="item-container">
      <div className="item-remove" onClick={remove}>
        ✖
      </div>
      <div className="item-text">{text}</div>
      <div className="item-check">✔</div>
    </div>
  );
}

export default ToDoItem;

 

만약 여기서도 remove() 이런 식으로 써준다면 erorr가 발생할 것입니다.

 

 


# 참고 자료

 

https://velopert.com/3480

 

React 기초 입문 프로젝트 – 흔하디 흔한 할 일 목록 만들기 | VELOPERT.LOG

이 포스트는 Fastcampus 의 리액트 강의 에서 사용된 강의 자료로서, 부연설명이 조금 생략되어있습니다. 기초가 부족하시다면 좀 오래되긴 했지만 저의 강의목록 에서 나오는 3편, 4편, 5편, 7편을 가볍게 읽고오세요 (해당 강의들의 실습은 따라하지 않으셔도 됩니다) 0. 시작하기 이번에는 프론트엔드 기초를 다룰때면 흔히 만들게 되는 투두 리스트, 혹은 “할 일 목록” 을 구현해보겠습니다. (우리는 앞으로 위와 같은 프로젝트를 만들어가게 됩니다) D

velopert.com

 

 


# 작성 록

2020.03.12

 오늘은 참고자료 없이 혼자서 해결을 해보고 싶었는데, 오류가 발생하다 보니 해결을 하기 위해서 또다시 찾아보게 되었습니다.

해당 문제를 피하기 위해서 지난 글에서 addEventListner를 사용했는데 이번엔 아녔습니다.

아무래도 배울 때 제가 직접 경험해보지 않고 타인이 알려주는 것으로 간접적으로 경험해보다 보니 조심성이 떨어졌던 것 같습니다.

이렇게 개발을 진행하는 중 오류가 생긴 것도 지속적인 기록을 통해서,

경험한 것 그리고 그로부터 파생될 수 있는 문제들에 대해서 주의를 기울여 추후에 발생하지 않도록 올바른 방향으로 나아갈  수 있게 해야겠습니다. 

'[WEB] > React' 카테고리의 다른 글

React - ToDoItem Check 기능 추가  (0) 2020.03.16
React - CSS작업  (0) 2020.03.16
React - To Do Item 추가 기능  (0) 2020.03.11
React - ToDoTemplate Component 추가  (0) 2020.03.09
React - 시계 Component 추가  (0) 2020.03.05