행복을 담는 블로그

[TIL] 240123(화) React 입문 개인과제 : To-do list 만들기(3) / 컴포넌트 분리하기 본문

TIL

[TIL] 240123(화) React 입문 개인과제 : To-do list 만들기(3) / 컴포넌트 분리하기

hyun0zin 2024. 1. 24. 00:03

🚨 컴포넌트로 분리하기

본격적으로 코드를 작성하기에 앞서 반복되는 컴포넌트를 먼저 분리하면 더 쉽게 코드를 작성할 수 있다.

(아니 사실 과제 할 때는 마지막에 컴포넌트 분리했는데,,, 분리하다가 계속 오류가 나서 죽을 뻔 했다,,,)

개인과제 해설 보고 깔끔하게 코드를 작성하기 위해서 컴포넌트를 열심히 분리해서 다시 작성해보았다.

크게는 App 컴포넌트 > Header / TodoController > TodoForm / TodoList > TodoCards
이렇게 여러개의 컴포넌트로 구성하였다.

 📌 component를 분리하면 꼭, import/ export를 사용하여 두 컴포넌트 사이를 연결 시켜줘야한다!!

1) App

각 구역 단위로 컴포넌트를 따로 분리하여 작성하니 App 컴포넌트의 코드가 아주 깔끔하게 가독성이 좋아졌다.

import React from "react";
import "./App.css";
import TodoHeader from "component/layout/Header";
import TodoController from "component/todo/TodoController";

const App = () => {
  return (
    <>
      <div className="app-style">
        <TodoHeader />
        <TodoController />
      </div>
    </>
  );
};

export default App;

2) Header

Header 부분은 딱히 크게 작성할 부분이 없었지만, 누가봐도 이곳은 header이다 라고 알 수 있도록, <header></header> 태그를 이용하여 작성한다.

const TodoHeader = () => {
  return (
    <header>
      <h1 className="headerStyle">My To-Do List</h1>
    </header>
  );
};

export default TodoHeader;

3) TodoController

TodoController에는 세 개의 컴포넌트를 연결시켜준다.
이때, TodoList는 Working...🔥Done🎉 두 부분에서 반복적으로 사용되므로, 하나의 TodoList 라는 컴포넌트를 생성하여 반복된 코드를 줄인다.

  • TodoForm : input 창과 button으로 이루어진 컴포넌트
  • TodoList : map 함수를 이용해 새로운 카드를 생성하고, Working과 Done의 두 부분에서 반복적으로 사용되는 컴포넌트

각각의 컴포넌트에 필요한 함수를 props로 넘겨준다.

import { useState } from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";

/* 초기 기본 card 객체 생성하기 */
const todoObj = {
  id: 1,
  title: "리액트 공부하기",
  text: "리액트 기초를 공부해봅시다.",
  isDone: false,
};


const TodoController = () => {

  /* 렌더링이 필요한 부분 State 생성하기 */
  const [cards, setCards] = useState([todoObj]);

  // input 태그 : title과 text 각각 입력 받아 state 생성
  const [title, setTitle] = useState("");
  const [text, setText] = useState("");


  const addTitleHandler = (event) => {
    setTitle(event.target.value);
  };

  const addTextHandler = (event) => {
    setText(event.target.value);
  };

/* TodoForm : card 추가하기 */
const addSubmit = (e) => {

    // form 태그의 자동 새로고침 막기
    e.preventDefault();

    // 빈 input 추가 막기
    if (!title || !text) {
      return;
    }

    // 새로운 카드 생성하기
    const newCards = {
      id: cards.length + 1,
      title,
      text,
      isDone: false,
    };
    setCards([...cards, newCards]);

    //처음 작성한 input 창 초기화
    setTitle("");
    setText("");

    e.target.reset();
  };

/* card Delete 버튼 */
const removeCardBtn = (id) => {
   const removeCards = cards.filter((card) => card.id !== id);
   setCards(removeCards);
  };

/* Done/Cancle 버튼 */
const updateCardBtn = (id) => {
   const updatedTodos = cards.map((todo) => {
      if (todo.id === id) {
        return {
          ...todo,
          isDone: !todo.isDone,
        };
      }
      return todo;
    });
    setCards(updatedTodos);
  };

/* TodoList 2가지 분리하여 작성 */  
const workingCards = cards.filter((card) => !card.isDone);
const doneCards = cards.filter((card) => card.isDone);

return (
    <main>
      <TodoForm
        addTitleHandler={addTitleHandler}
        addTextHandler={addTextHandler}
        addSubmit={addSubmit}
      />
      <TodoList
        subTitle="Working...🔥"
        cards={workingCards}
        removeCardBtn={removeCardBtn}
        updateCardBtn={updateCardBtn}
      />
      <TodoList
        subTitle="Done🎉"
        cards={doneCards}
        removeCardBtn={removeCardBtn}
        updateCardBtn={updateCardBtn}
      />
    </main>
  );
};

export default TodoController;

4) TodoForm

TodoForm 컴포넌트에는 title과 content를 입력할 수 있는 input 창 2개와 새로운 카드를 추가할 수 있는 button으로 이루어져 있다.

  • <form></form> 태그를 사용하여 keyboard에서 키를 눌렀을 때 submit event가 발생한다.
  • 필요한 key와 함수를 props로 받는다.
const TodoForm = ({ title, text, addTitleHandler, addTextHandler, addSubmit }) => {
  return (
    <form className="inputStyle" onSubmit={addSubmit}>
      Title <input className="titleClass" value={title} onChange={addTitleHandler} />
      Content <input className="textClass" value={text} onChange={addTextHandler} />
      <button id="addBtn" type="submit" className="btn btn-outline-dark">
        Add
      </button>
    </form>
  );
};

export default TodoForm;

5) TodoList

TodoList는 TodoController에서 두 번 사용되는 컴포넌트이다.

  • cards를 map 함수를 이용하여 새로운 배열로 만들어 새로운 list를 구성한다.
  • 각각의 cards는 새로운 TodoCards를 이용하여 생성한다.
import TodoCards from "./TodoCards";

const TodoList = ({ cards, subTitle, removeCardBtn, updateCardBtn }) => {
  return (
    <section className="cardSection">
      <h2>{subTitle}</h2>
      <ul className="card-container">
        {cards.map(function (item) {
          return (
            <TodoCards
              key={item.id}
              item={item}
              removeCardBtn={removeCardBtn}
              updateCardBtn={updateCardBtn}
            />
          );
        })}
      </ul>
    </section>
  );
};

export default TodoList;

6) TodoCards

생성한 card의 id, title, text, isDone을 props로 넘겨 받아 각각의 필요한 위치에서 받아서 card를 생성한다.

  • Done과 Cancle의 버튼의 경우 삼항 연산자를 사용하여 버튼의 이름을 생성한다.
  • isDone이 false이면 Done으로, isDone이 true이면 Cancle로 버튼을 생성한다.
const TodoCards = ({ item, removeCardBtn, updateCardBtn }) => {
  const { id, title, text, isDone } = item;

  return (
    <div key={id} className="cardList">
      <h2>{title}</h2>
      <p>{text}</p>
      <button
        id="removeBtn"
        type="button"
        className="btn btn-danger"
        onClick={() => removeCardBtn(id)}
      >
        Delete
      </button>
      &nbsp;
      <button
        id="completeBtn"
        type="button"
        className="btn btn-success"
        onClick={() => updateCardBtn(id)}
      >
        {isDone ? "Cancle" : "Done"}
      </button>
    </div>
  );
};

export default TodoCards;