What I Learnd/WIL

WIL - useState 사용해서 To do list 만들기

키싸 2023. 6. 20. 00:19

지난 한주는 React를 배우기 시작했다.
입문강의를 듣고 과제까지 하면서 느낀점은 리액트는 확실히 직관적인 언어구나.

물론 여전히 여럽지만, 직관적으로 문제를 풀어가면서 개발이 점점 더 재밌어진다.

이번주 과제에서는 리액트로 투두리스트를 만들었는데, 과제를 만들면서 to do를 구성하는 initail state를 만들고 이를 mapping, 그리고 filtering 하는 데 어려움이 있었다. 아무래도 충분히 이해하지 못하고 과제를 진행했던 것 같다.

하지만 컴포넌트를 따로 만들어주고 각각의 이벤트핸들러를 생성해서 적용해주어 원하는 기능을 구현했다.


1. 메인 컴포턴트에서는 전체적인 레이아웃과 상태를 관리해줬다.

  • 여기서 useState를 사용해 리렌더링 되어야 하는 부분의 각 state를 생성해주고,
  • 각 버튼에 해당하는 이벤트 핸들러를 생성해서 카드를 추가, 삭제, 완료, 완료취소 하는 기능을 만들어줬다.
  • 각 이벤트 핸들러에서는 filter와 find 메소드를 사용해서 클릭한 값과 다르거나 같은 카드를 찾아줬고
  • useState로 리렌더링 해줬다. 
function App() {
  const [title, setTitle] = useState(""); // 1. task 리렌더링을 위한 상태변수와 상태변경함수, useState함수 세팅
  const [context, setContext] = useState("");
  const [cards, setCards] = useState([]); // cards 영역, array는 []로 처리
  const [doneCards, setDoneCards] = useState([]); // 완료cards 영역

  const onSubmitHandler = () => {
    setCards([...cards, { title, context }]); // distructuring 후 신규 카드 추가
    setTitle(""); // title 필드 초기화
    setContext(""); // context 필드 초기화
  };

  // card.title과 클릭한 카드의 타이틀이 같지않거나
  // card.context와 클릭한 카드 내용이 같지 않은 것들을 골라서
  // 즉, 전체 카드들을 돌면서 클릭한 카드와 다른 값을 가진 카드들을 리스트업하고
  // 그걸 setCards로 리렌더링해준다.
  const onDeleteHandler = (cardTitle, cardContext) => {
    const updatedCards = cards.filter(
      (card) => card.title !== cardTitle || card.context !== cardContext
    );

    setCards(updatedCards);
  };

  // 위와 같이 클릭한 카드와 값이 다른 카드들을 골라서 필터링하고 다시 setCards 해주고
  // 완료된 카드 목록에는 클릭한 카드와 내용이 같은 카드를 찾아서
  // setDoneCard에 기존 값을 distructuring 후 찾은 카드를 추가해서 다시 리스트업, 렌더링 해준다!
  const onDoneHandler = (cardTitle, cardContext) => {
    const updatedCards = cards.filter(
      (card) => card.title !== cardTitle || card.context !== cardContext
    );
    const doneCard = cards.find(
      (card) => card.title === cardTitle && card.context === cardContext
    );

    setCards(updatedCards);
    setDoneCards([...doneCards, doneCard]);
  };

  const onUnDoneHandler = (cardTitle, cardContext) => {
    const updatedDoneCards = doneCards.filter(
      (card) => card.title !== cardTitle || card.context !== cardContext
    );
    const undoneCard = doneCards.find(
      (card) => card.title === cardTitle && card.context === cardContext
    );
    setDoneCards(updatedDoneCards);
    setCards([...cards, undoneCard]);
  };

  const onDeleteHandlerForUnDone = (cardTitle, cardContext) => {
    const updatedDoneCards = doneCards.filter(
      (card) => card.title !== cardTitle || card.context !== cardContext
    );

    setDoneCards(updatedDoneCards);
  };

return (
    <div>
      <div>
        <header>
          <div >My Task List</div>
        </header>
        <div>
          <div>Task</div>
          <div>
            <textarea
              value={title}
              type="text"
              className="inputTitle"
              placeholder=" 제목"
              onChange={(event) => setTitle(event.target.value)}
              id="myTitlearea"
            />
          </div>
          <div>
            <textarea
              value={context}
              type="text"
              className="inputContext"
              placeholder=" 내용"
              onChange={(event) => setContext(event.target.value)}
              id="myContextarea"
            />
          </div>

          <button className="mainBtn" onClick={onSubmitHandler}>
            추가하기
          </button>
        </div>
        <div>
          <div>
            <div>WORKING</div>
            {cards.map((card, index) => (
              <Cards
                key={index}
                title={card.title}
                context={card.context}
                onDelete={onDeleteHandler}
                onDone={onDoneHandler}
              />
            ))}
          </div>

          <div>
            <div>DONE</div>
            {doneCards.map((card, index) => (
              <DoneCards
                key={index}
                title={card.title}
                context={card.context}
                onDelete={onDeleteHandlerForUnDone}
                onUnDone={onUnDoneHandler}
              />
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

 

2. 생성된 카드를 보여주는 컴포넌트와  완료된 카드리스트를 보여주는 컴포넌트를  추가로 만들었다.

  • 구조분해할당을 사용해 부모 컴포넌트인 App 컴포넌트에서 전달한 속성값을 받아와 내용을 보여줬고
  • 삭제와 완료버튼을 만들어 각 버튼에 해당되는 콜백함수를 만들어줬다.
  • 여기서 부모 컴포넌트에 있는 onDelHandler, onDoneHandler ~~~ 함수가 작동될 때마다(버튼을 누를 때마다)  그 함수는 onDelete, onDone 콜백함수를 호출하고, 결국 최종적으로는 이 차일드 컴포넌트의 콜백함수를 통해 title, context를 인수(함수를 호출할 때 전달되는 값들)로 부모 컴포넌트의 함수에 전달해준다!
function Cards({ title, context, onDelete, onDone }) {	// 구조분해할당
  const onDelHandler = () => {
    onDelete(title, context);
  };	// 콜백함수

  const onDoneHandler = () => {
    onDone(title, context);
  };	// 콜백함수

  return (
    <div>
      <div>{title}</div>
      <div>{context}</div>
      <div>
        <button onClick={onDelHandler}>
          삭제하기
        </button>
        <button onClick={onDoneHandler}>
          완료
        </button>
      </div>
    </div>
  );
}

// 완료 카드
function DoneCards({ title, context, onDelete, onUnDone }) {
  const onDelHandler = () => {
    onDelete(title, context);
  };

  const onUnDoneHandler = () => {
    onUnDone(title, context);
  };

  return (
    <div>
      <div>{title}</div>
      <div>{context}</div>
      <div>
        <buttononClick={onDelHandler}>
          삭제하기
        </button>
        <button onClick={onUnDoneHandler}>
          완료취소
        </button>{
      </div>
    </div>
  );
}

 

근데 이렇게 해서 나는 id 값을 랜덤리 지정해주지 않아도 기능을 구현했다. 그치만 다시 만들어보면서는 initial state 값을 만들고, uuid 라이브러리를 써서 유니크한 id값도 줘봐야겠다.