[나작프] 003. Todo List(4)

김현주·2022년 5월 26일
0

나의작은프로젝트

목록 보기
8/9

1. todo item 삭제 구현

1-1) ❌버튼 클릭시 item이 삭제되도록

// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';

function TodoItem({ todoItem, todoList, setTodoList }){
  function removeItem(e){
    const parentLi = e.target.parentElement; // (1)
    parentLi.remove(); // (1)-1
  }
  
  return(
    <>
      <li className={itemStyle.item}>
        <input type='checkbox' />
        <span>{ todoItem }</span>
        <button type='button' className={itemStyle.editButton}>🔺</button>
        <button 
          type='button' 
          className={itemStyle.deleteButton}
          onClick={removeItem} // (2)
        ></button>
      </li>
    </>
  )
}
TodoItem.PropTypes = {
  todoItem: PropTypes.shape({
    id: PropTypes.number.isRequired,
    todo: PropTyes.string.isRequired,
  }),
  todoList: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      todo: PropTyes.string.isRequired,
    })
  ),
  setTodoList: PropTypes.func.isRequired,
};
export default TodoItem;

(1) click하는 태그의 부모요소를 찾는다.
(1)-1 부모요소를 삭제한다.
(2) ❌버튼을 클릭하면 removeItem 메서드가 실행


2. todo item 수정 구현

  • 🔺 버튼을 클릭하면 item을 수정하는 input과 ✔️이 나온다.
  • ✔️ 버튼을 클릭하면 item 수정이 완료되며 다시 🔺버튼으로 돌아온다.

2-1) 🔺버튼 클릭시 edit의 상태 변환

// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';
import { useState } from 'react';

function TodoItem({ todoItem, todoList, setTodoList }){
  ...
  const [edit, setEdit] = useState(false); // (1)
  function editItemState(){ // (2)
    setEdit(true); // (2)-1
  }
  
  return(
    <>
      <li className={itemStyle.item}>
        <input type='checkbox' />
        <span>{ todoItem }</span>
        <button 
          type='button' 
          className={itemStyle.editButton}
          onClick={editItemState} // (3)
        >🔺</button>
        <button 
          type='button' 
          className={itemStyle.deleteButton}
          onClick={removeItem}
        ></button>
      </li>
    </>
  )
}
TodoItem.PropTypes = {
  ...
};
export default TodoItem;

(1) edit상태를 담을 useState 생성 (기본값 false)
(2) edit상태를 false에서 true로 변환하는 메서드 생성
(2)-1 setEdit setter함수를 사용하여 true로 변환
(3) 🔺 버튼을 클릭할 때 editItemState 메서드가 동작

2-2) 🔺버튼 클릭시 ✔️버튼으로 바뀌도록

// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';
import { useState } from 'react';

function TodoItem({ todoItem, todoList, setTodoList }){
  ...
  const [edit, setEdit] = useState(false);
  function editItemState(){
    setEdit(true);
  }
  
  return(
    <>
      <li className={itemStyle.item}>
        <input type='checkbox' />
        <span>{ todoItem }</span>
        { // (1)
          edit === false ? 
            (
              <button 
                type='button' 
                className={itemStyle.editButton}
                onClick={editItemState}
              >🔺</button>
            ) : 
            (
              <button 
                type="button" 
                className={itemStyle.editButton} 
              >✔️</button>
            )
        }
        <button
          type='button' 
          className={itemStyle.deleteButton}
          onClick={removeItem}
        ></button>
      </li>
    </>
  )
}
TodoItem.PropTypes = {
  ...
};
export default TodoItem;

(1) 삼항연산자를 사용하여 edit이 false일 경우 🔺버튼, edit이 true일 경우 ✔️버튼 → toggle 버튼

2-3) 수정될 input 구현

// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';
import { useState } from 'react';

function TodoItem({ todoItem, todoList, setTodoList }){
  ...
  const [edit, setEdit] = useState(false);
  const [newTodo, setNewTodo] = useState(todoItem.todo); // (1)
  
  function editItemState(){
    setEdit(true);
  }
  function editCompleteItem(){ // (2)
    const newEditTodoList = todoList.map((item) => ({ // (2)-1
      ...item, // (2)-2
      todo: item.id === todoItem.id ? newTodo : item.todo // (2)-3
    }));
    setTodoList(newEditTodoList); // (3)
    setEdit(false); // (4)
  }
  
  return(
    <>
      <li className={itemStyle.item}>
        <input type='checkbox' />
        <span>{ todoItem }</span>
        {
          edit === false ? 
            (
              <button 
                type='button' 
                className={itemStyle.editButton}
                onClick={editItemState}
              >🔺</button>
            ) : 
            (
              <button 
                type="button" 
                className={itemStyle.editButton} 
                onClick={editCompleteItem} // (5)
              >✔️</button>
            )
        }
        <button
          type='button' 
          className={itemStyle.deleteButton}
          onClick={removeItem}
        ></button>
      </li>
    </>
  )
}
TodoItem.PropTypes = {
  ...
};
export default TodoItem;

(1) 수정할 todo를 담을 useState를 생성 (기본값은 todoItem.todo(원래담겨있던내용))
(2) editCompleteItem 메서드 생성
(2)-1 todoList을 반복하는 newEditTodoList 객체 생성
(2)-2 todoList에 있던 기존의 item들
(2)-3 새로운 item을 넣음(삼항연산자 사용)
(3) 새로운 리스트를 넣어줌
(4) edit의 상태를 false로 변경
(5) ✔️버튼 클릭시 (2)의 editCompleteItem 메서드 실행

2-4) 수정될 때 input에 focus되도록

// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';
import { useState, useRef, useEffect } from 'react';

function TodoItem({ todoItem, todoList, setTodoList }){
  ...
  const [edit, setEdit] = useState(false);
  const [newTodo, setNewTodo] = useState(todoItem.todo);
  const editInputRef = useRef(null); // (1)
  
  function editItemState(){
    setEdit(true);
  }
  function editCompleteItem(){
    const newEditTodoList = todoList.map((item) => ({
      ...item,
      todo: item.id === todoItem.id ? newTodo : item.todo
    }));
    setTodoList(newEditTodoList);
    setEdit(false);
  }
  function editInput(e){ // (2)
    e.target.value;
  }
  useEffect(() => { // (3)
    if(edit == true){ // (3)-1
      editInputRef.current.focus(); // (3)-2
    }
  }, [edit]); // (3)-3
  
  return(
    <>
      <li className={itemStyle.item}>
        <input type='checkbox' />
        {
          edit === true ? // (4)
            (
              <input 
                type='text'
                className={itemStyle.editInputbox}
                value={newTodo}
                onChange={editInput} // (5)
                ref={editInputRef} // (6)
              />
            ) :
            (
              <span>{ todoItem }</span>
            )
        }
        {
          edit === false ? 
            (
              <button 
                type='button' 
                className={itemStyle.editButton}
                onClick={editItemState}
              >🔺</button>
            ) : 
            (
              <button 
                type="button" 
                className={itemStyle.editButton} 
                onClick={editCompleteItem}
              >✔️</button>
            )
        }
        <button
          type='button' 
          className={itemStyle.deleteButton}
          onClick={removeItem}
        ></button>
      </li>
    </>
  )
}
TodoItem.PropTypes = {
  ...
};
export default TodoItem;

(1) useRef Hook을 사용하여 editInputRef 객체 생성
(2) input의 값이 변할때마다 값을 가져오는 editInput 메서드 생성
(3) useEffect Hook 사용
(3)-1 edit의 상태가 true일 때
(3)-2 editInputRef를 가르키는 DOM을 focus
(3)-3 edit이 실행될 때 마다

⭐ useEffect()?
→ 컴포넌트가 렌더링된 이후 다양한 side effect를 표현할 수 있다.
→ class 생명주기 메서드인 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것으로 생각해도 좋다.
→ effect를 실행하고 이를 정리(clean-up)하는 과정을 딱 한 번씩만 실행하고 싶다면, 빈 배열([ ])을 두 번째 인수로 넘기면 된다.
출처 : https://ko.reactjs.org/docs/hooks-effect.html

(4) 변경될 input이 보여질지 원래의 todo내용이 보여질지 삼항연산자를 사용
(5) input의 내용이 바뀔 때 마다 (2)의 editInput 메서드 사용
(6) ref를 사용하여 input에 focus되도록


3. 결과

profile
✨프론트엔드 개발자가 되기 위한 독학러✨

0개의 댓글