프로젝트

[리액트] 리액트로 ToDoList 만들기 - 토글 기능

ejunyang 2024. 5. 20. 19:29

 

 

 

 

https://ejunyang.tistory.com/entry/PP-%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A1%9C-ToDoList-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%82%AD%EC%A0%9C-%EA%B8%B0%EB%8A%A5

 

[P.P] 리액트로 ToDoList 만들기 - 삭제 기능

https://ejunyang.tistory.com/entry/PP-%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A1%9C-ToDoList-%EB%A7%8C%EB%93%A4%EA%B8%B0 [P.P] 리액트로 ToDoList 만들기 - UI구현, 리스트 추가또 다른 프로젝트가 시작됐다 ㅎ 저번주 금요일에 리액트

ejunyang.tistory.com

 

 

 

UI 구현

  • Todo 추가 하기
  • Todo 삭제 하기
  • Todo 완료/취소 상태 변경하기 (진행중 ↔ 완료)

 

기능 구현

  • 제목과 내용을 입력하고, [추가하기] 버튼을 클릭하면 Working에 새로운 Todo가 추가되고 제목 input과 내용 input은 다시 빈 값으로 바뀌도록 구성해주세요.
  • [삭제하기] 버튼을 클릭하면 Working 또는 Done 에 있는 것과 상관없이 삭제처리가 되도록 해주세요.
  • Todo의 isDone 상태가 true이면, 상태 버튼의 라벨을 취소, isDone이 false 이면 라벨을 완료 로 조건부 렌더링 해주세요.
  • Todo의 상태가 Working 이면 위쪽에 위치하고, Done이면 아래쪽에 위치하도록 구현합니다.
  • Layout의 최대 너비는 1200px, 최소 너비는 800px로 제한하고, 전체 화면의 가운데로 정렬해주세요.
  • 컴포넌트 구조는 자유롭게 구현해보세요.

 

 

🍯 HINT

  • 사용한 hook은 오직 useState
  • 기능 구현을 위해 생성한 함수는 2개 입니다. onChangeHandler , onSubmitHandler
  • 사용한 javascript 내장 메서드는 map, filter 입니다.
  • todo의 initial state는 {id: 0, title: “”, body: “”, isDone: false} 입니다.

 


 

 

컴포넌트

TodoTemplate.jsx  화면을 가운데에 정렬시켜주며, 일정 리스트를 보여줍니다. children으로 내부 JSX를 props로 받아 와서 렌더링해줍니다.
TodoAdd.jsx 할 일을 추가하는 컴포넌트 입니다.
TodoList.jsx todos 배열을 props로 받아 온 후, 이를 배열 내장 함수 map을 사용해서 여러 개의 TodoItem 컴포넌트로 변환하여 보여 줍니다.
TodoItem.jsx 진행중인 할 일 정보를 보여주는 컴포넌트입니다. todo 객체를 props로 받아 와서 상태에 따라 다른 스타일의 UI를 보여줍니다
CurrentDate.jsx 현재 날짜를 알려주는 컴포넌트입니다.
Icons.jsx 배경에 꾸며주는 Sticker 요소를 보여주는 컴포넌트입니다.

 


 

 

App.jsx

Todo의 isDone 상태가 true이면, 상태 버튼의 라벨을 취소, isDone이 false 이면 라벨을 완료 로 조건부 렌더링이 필요해서 토글 함수를 만들었다. 삭제 함수와 마찬가지로 id 값을 받아와서 처리하도록 했다.

 

map과 삼항연산자를 활용했다. map()으로 배열 순회를 돌면서 배열의 id와 사용자가 선택한 id 가 같다면, todo에 isDone 키의 값이 true -> false, false -> true 로 반전시켜주는 NOT 연산자를 사용해줬다. 같지 않다면 그대로 todo가 나오도록 해주었다.

const onToggle = (id) => {
    setTodo(
      todos.map((todo) =>
        todo.id === id ? { ...todo, isDone: !todo.isDone } : todo
      )
    );
  };

 

TodoList 에서 사용할 수 있게 props로 내려준다. 그리고 진행중인 리스트와 진행 완료된 리스트를 나누어서 완료된 리스트는 진행 중인 리스트 아래로 내려야 하기때문에 TodoList 를 두개 만들어주고 안에 들어가는 props 에 filter를 써서 조건부 렌더링이 되도록 해주었다.

const workingTodo = todos.filter((todo) => !todo.isDone);
const doneTodo = todos.filter((todo) => todo.isDone);
return (
    <>
      <TodoTemplate>
        <CurrentDate todos={todos} />
        <TodoList
          key={todos.id}
          todos={workingTodo}
          onRemove={onRemove}
          onToggle={onToggle}
        />
        <TodoList
          key={todos.id}
          todos={doneTodo}
          onRemove={onRemove}
          onToggle={onToggle}
        />
        <TodoAdd onInsert={onInsert} />
      </TodoTemplate>
    </>
  );

 

 

 

TodoList.jsx

TodoItem에서 사용할 수 있도록 props를 내려주자.

const TodoList = ({ todos, onRemove, onToggle }) => {
  return (
    <div>
      {todos.map((todo) => {
        return (
          <TodoItem
            todo={todo}
            key={todo.id}
            onRemove={onRemove}
            onToggle={onToggle}
          />
        );
      })}
    </div>
  );
};

 

 

 

TodoItem.jsx

isDone 이 true 일때 체크 아이콘은 ✔️ 상태를 유지하고, isDone 이 false 라면 O 체크를 지운 아이콘을 유지하도록 했다.

{isDone && (<IoCheckmark className="check" onClick={() => onToggle(id)} />)}
{!isDone && (<PiCircleLight className="check" onClick={() => onToggle(id)} />)}

 

이렇게 코드를 적고 실행을 했는데 오류가 생겼다. isDone is not defined. 

 

코드를 확인해보니 구조할당분해에서 isDone을 빼먹어서 생긴 오류였다. isDone 을 넣어주니 정상적으로 작동하였다.

const { id, contents, isDone } = todo;