memostack
article thumbnail
블로그를 이전하였습니다. 2023년 11월부터 https://bluemiv.tistory.com/에서 블로그를 운영하려고 합니다. 앞으로 해당 블로그의 댓글은 읽지 못할 수 도 있으니 양해바랍니다.
반응형

아이템 내용 수정 기능 개발

'연필' 모양의 버튼을 눌렀을때 해당 아이템의 내용이 <input /> 태그로 변하도록한다.

  • 그리고, 입력을 받아 아이템의 내용을 변경할 수 있도록 한다.
  • 연필 모양의 버튼은 submit 버튼으로 변하도록 한다.

 

버튼 클릭시 input 태그로 변하도록 하기

ToDoItem.jsx

수정 버튼을 눌렀을때, edit 모드가 토글 되도록 함

그리고 수정 버튼은 👌 버튼이 되도록 한다.

const ToDoItem = ({ todoItem, todoList, setTodoList }) => {
  const [edited, setEdited] = useState(false); // 수정 모드인지 확인하기 위한 플래그 값
  ...
  
  const onClickEditButton = () => {
    // 클릭시 edited 값을 true로 바꿈
    setEdited(true);
  };
  
  return (
    <li className="todoapp__item">
      ...
      {
        // 수정 버튼
        // 완료한 일인 경우에는 null을 반환하여 보이지 않도록 함
        !todoItem.checked ? (
          edited ? (
            <button
              type="button"
              className="todoapp__item-edit-btn"
            >
              👌
            </button>
          ) : (
            <button
              type="button"
              className="todoapp__item-edit-btn"
              onClick={onClickEditButton}
            >
              ✏
            </button>
          )
        ) : null
      }
      ...
    </li>
  );
}

 

수정 기능 만들기

이번엔 input 태그에 새로운 아이템 내용을 입력하여 👌 버튼을 누르면 내용이 변하도록 해보자

const ToDoItem = ({ todoItem, todoList, setTodoList }) => {
  const [edited, setEdited] = useState(false);
  const [newText, setNewTest] = useState(todoItem.text); // 새로운 아이템 내용
  ...
  
  const onChangeEditInput = (e) => {
    setNewTest(e.target.value);
  };

  const onClickSubmitButton = () => {
    const nextTodoList = todoList.map((item) => ({
      ...item,
      text: item.id === todoItem.id ? newText : item.text, // 새로운 아이템 내용을 넣어줌
    }));
    setTodoList(nextTodoList); // 새로운 리스트를 넣어줌

    setEdited(false); // 수정모드를 다시 읽기모드로 변경
  };
  ...
  
  return (
    <li className="todoapp__item">
    ...
      {
        // 수정 버튼
        // 완료한 일인 경우에는 null을 반환하여 보이지 않도록 함
        !todoItem.checked ? (
          edited ? (
            <button
              type="button"
              className="todoapp__item-edit-btn"
              onClick={onClickSubmitButton} // 클릭 이벤트 발생시 onClickSubmitButton 수행
            >
              👌
            </button>
          ) : (
            <button
              type="button"
              className="todoapp__item-edit-btn"
              onClick={onClickEditButton}
            >
              ✏
            </button>
          )
        ) : null
      }

    </li>
  );
};

 

포커싱

수정버튼을 누르면 input 태그에 focusing이 되도록 하자

const ToDoItem = ({ todoItem, todoList, setTodoList }) => {
  ...
  const editInputRef = useRef(null);
  
  useEffect(() => {
    // edit 모드일때 포커싱을 한다.
    if (edited) {
      editInputRef.current.focus();
    }
  }, [edited]);

  ...
  return (
    <li className="todoapp__item">
      ....
      {
        // 아이템 내용
        edited ? (
          <input
            type="text"
            value={newText}
            ref={editInputRef} // ref 로 DOM에 접근
            onChange={onChangeEditInput}
          />
        ) : (
          <span
            className={`todoapp__item-ctx ${
              todoItem.checked ? 'todoapp__item-ctx-checked' : ''
            }`}
          >
            {todoItem.text}
          </span>
        )
      }
      ...
    </li>
  );
};

 

스타일

그리고 edit input 태그에 스타일을 입혀보자

...
.todoapp__item-edit-input {
  flex: 1;
  border: none;
  border-bottom: 1px solid #f1f3f5;
  padding: 5px;
  font-size: 1em;
  box-sizing: border-box;
}
.todoapp__item-edit-input:focus {
  outline: none;
}
...

 

전체코드

import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';

const ToDoItem = ({ todoItem, todoList, setTodoList }) => {
  const [edited, setEdited] = useState(false);
  const [newText, setNewTest] = useState(todoItem.text);

  const editInputRef = useRef(null);

  useEffect(() => {
    // edit 모드일때 포커싱을 한다.
    if (edited) {
      editInputRef.current.focus();
    }
  }, [edited]);

  const onChangeCheckbox = () => {
    const nextTodoList = todoList.map((item) => ({
      ...item,
      // id 값이 같은 항목의 checked 값을 Toggle 함
      checked: item.id === todoItem.id ? !item.checked : item.checked,
    }));

    setTodoList(nextTodoList);
  };

  const onClickEditButton = () => {
    setEdited(true);
  };

  const onChangeEditInput = (e) => {
    setNewTest(e.target.value);
  };

  const onClickSubmitButton = () => {
    const nextTodoList = todoList.map((item) => ({
      ...item,
      text: item.id === todoItem.id ? newText : item.text, // 새로운 아이템 내용을 넣어줌
    }));
    setTodoList(nextTodoList);

    setEdited(false);
  };

  return (
    <li className="todoapp__item">
      {/* 아이템 완료 체크 / 체크 해제를 위한 체크박스 */}
      <input
        type="checkbox"
        className="todoapp__item-checkbox"
        checked={todoItem.checked}
        onChange={onChangeCheckbox}
      />
      {
        // 아이템 내용
        edited ? (
          <input
            type="text"
            className="todoapp__item-edit-input"
            value={newText}
            ref={editInputRef}
            onChange={onChangeEditInput}
          />
        ) : (
          <span
            className={`todoapp__item-ctx ${
              todoItem.checked ? 'todoapp__item-ctx-checked' : ''
            }`}
          >
            {todoItem.text}
          </span>
        )
      }
      {
        // 수정 버튼
        // 완료한 일인 경우에는 null을 반환하여 보이지 않도록 함
        !todoItem.checked ? (
          edited ? (
            <button
              type="button"
              className="todoapp__item-edit-btn"
              onClick={onClickSubmitButton}
            >
              👌
            </button>
          ) : (
            <button
              type="button"
              className="todoapp__item-edit-btn"
              onClick={onClickEditButton}
            >
              ✏
            </button>
          )
        ) : null
      }

      {/* 삭제 버튼 */}
      <button type="button" className="todoapp__item-delete-btn">
        🗑
      </button>
    </li>
  );
};

ToDoItem.propTypes = {
  todoItem: PropTypes.shape({
    id: PropTypes.number,
    text: PropTypes.string.isRequired,
  }),
  todoList: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      text: PropTypes.string.isRequired,
    })
  ),
  setTodoList: PropTypes.func.isRequired,
};

export default ToDoItem;

 

결과 화면

결과 화면

 

전체 코드는 아래 링크에서 확인 할 수 있습니다.

https://github.com/bluemiv/react_todo_app

 

GitHub - bluemiv/react_todo_app: React로 만든 ToDo Web Application

React로 만든 ToDo Web Application. Contribute to bluemiv/react_todo_app development by creating an account on GitHub.

github.com

 
반응형
블로그를 이전하였습니다. 2023년 11월부터 https://bluemiv.tistory.com/에서 블로그를 운영하려고 합니다. 앞으로 해당 블로그의 댓글은 읽지 못할 수 도 있으니 양해바랍니다.
profile

memostack

@bluemiv_mm

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!