포스트

2024.06.21 프로그래머스 - React로 만드는 간단 ToDo 2

React로 ToDoList 만들어보기 2

React의 Props

  • React에서 Props는 부모 컴포넌트에서 자식 컴포넌트로의 을 전달하기 위한 객체이다.
  • 함수 또한 일급 객체이므로 으로써 props 통신에 포함 된다.
  • props는 함수형 컴포넌트에서는 인수의 형태, 클래스형 컴포넌트에서는 객체의 속성의 형태로 호출 된다.

완료한 Todo 코드

App.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import { useState } from "react";
import "./App.css";
import TodoListContainer from "./components/TodoListContainer";
import TodoGenerator from "./components/TodoGenerator";
import Clock from "./components/Clock";
import MyWeather from "./components/MyWeather";

export type TodoType = {
  id: number;
  text: string;
  isChecked: boolean;
};

const initialTodos: TodoType[] = [
  {
    id: 1,
    text: "공부하기",
    isChecked: false,
  },
  {
    id: 2,
    text: "잠자기",
    isChecked: false,
  },
  {
    id: 3,
    text: "밥먹기",
    isChecked: false,
  },
];

function App() {
  const [todos, setTodos] = useState<TodoType[]>(initialTodos);

  function checkHandler(todoId: number) {
    setTodos((prevTodos) =>
      prevTodos.map((item) =>
        item.id === todoId ? { ...item, isChecked: !item.isChecked } : item
      )
    );
  }

  function addTodoItem(text: string) {
    setTodos((prevTodos) => [
      ...prevTodos,
      { id: Math.random(), text: text, isChecked: false },
    ]);
  }

  function removeTodoItem(todoId: number) {
    setTodos((prevTodos) => {
      const filteredTodos = prevTodos.filter((todo) => todo.id !== todoId);
      return [...filteredTodos];
    });
  }

  const title = "오늘 할 일";

  return (
    <>
      <div className="container">
        <h1>{title}</h1>
        <TodoGenerator addTodoHandler={addTodoItem} />
        <Clock />
        <TodoListContainer
          todos={todos}
          checkHandlerFunc={checkHandler}
          removeTodo={removeTodoItem}
        />
        <MyWeather weather="맑음">일기예보</MyWeather>
      </div>
    </>
  );
}

export default App;

  • App.tsx에서는 todos 에 대한 전역적인? 상태관리를 가능하도록 구성하였다.
  • App.tsx에서 todos를 수정하는 함수를 작성하여, 실제로 해당 변경이 일어나는 자식 컴포넌트까지 props를 전달하였다.

TodoGenerator.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { useState, ChangeEvent } from "react";
import { Button } from "react-bootstrap";

type TodoGeneratorProps = {
  addTodoHandler: (text: string) => void;
};

function TodoGenerator({ addTodoHandler }: TodoGeneratorProps): JSX.Element {
  const [newTodo, setNewTodo] = useState<string>("");

  function changeTodoHandler(e: ChangeEvent<HTMLInputElement>) {
    setNewTodo(e.target.value);
  }

  return (
    <div className="todo-generator">
      <input type="text" onChange={(e) => changeTodoHandler(e)} />
      <Button
        as="button"
        variant="primary"
        onClick={() => addTodoHandler(newTodo)}
      >
        생성
      </Button>
    </div>
  );
}

export default TodoGenerator;

  • 해당 컴포넌트는 새로운 Todo를 생성하는 기능을 하는 컴포넌트이다.
  • App.tsx에서 전달받은 함수 props값인 addTodoHandler를 통해, 해당 자식 컴포넌트에서 상위 컴포넌트의 state인 todos의 상태를 변경할 수 있도록 구성하였다.

TodoListContainer.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import TodoListItem from "./TodoListItem";

type TodoType = {
  id: number;
  text: string;
  isChecked: boolean;
};

type TodoListContainerProps = {
  todos: TodoType[];
  checkHandlerFunc: (todoId: number) => void;
  removeTodo: (id: number) => void;
};

function TodoListContainer({
  todos,
  checkHandlerFunc,
  removeTodo,
}: TodoListContainerProps): JSX.Element {
  return (
    <div className="container">
      <div className="container">
        <div className="board">
          <ul className="todo-list">
            {todos.map((todo) => (
              <TodoListItem
                key={todo.id}
                onRemove={removeTodo}
                todoId={todo.id}
                text={todo.text}
                onChecked={checkHandlerFunc}
                isChecked={todo.isChecked}
              />
            ))}
          </ul>
        </div>
      </div>
    </div>
  );
}

export default TodoListContainer;

  • 실제 todo 목록을 표현하는 container 컴포넌트이다.
  • App.tsx에서 전달받은 todos를 기반으로 TodoListItem 컴포넌트를 리스트의 형태로 렌더링
  • App.tsx에서 전달받은 removeTodo 함수를 TodoListItem 컴포넌트 까지 전달한다.

TodoListItem.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import { useState } from "react";
import { Button } from "react-bootstrap";
import TodoModal from "./TodoModal";

type TodoListItemType = {
  text: string;
  todoId: number;
  onChecked: (todoId: number) => void;
  isChecked: boolean;
  onRemove: (todoId: number) => void;
};

function TodoListItem({
  todoId,
  text,
  onChecked,
  isChecked,
  onRemove,
}: TodoListItemType) {
  const [showModal, setShowModal] = useState<boolean>(false);

  function handleShowModal() {
    setShowModal(true);
  }

  function handleCloseModal() {
    setShowModal(false);
  }

  return (
    <li className="todo-list-item">
      <input type="checkbox" className="" onChange={() => onChecked(todoId)} />
      {!isChecked && <span>{text}</span>}
      {isChecked && <del>{text}</del>}
      <Button variant="secondary" size="sm" onClick={() => onRemove(todoId)}>
        삭제
      </Button>
      <Button variant="info" onClick={handleShowModal}>
        정보
      </Button>
      <TodoModal show={showModal} handleClose={handleCloseModal} text={text} />
    </li>
  );
}

export default TodoListItem;

  • Todo 목록 한 개에 대한 컴포넌트
  • 모달창 표시에 대한 여부를 통제하는 showModal state를 통해 모달창을 제어 한다.
  • props로 전달받은 onRemove 함수를 통해, todo 목록 한 개에 대한 삭제를 진행할 수 있다.

TodoModal.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { Modal, Button } from "react-bootstrap";

type TodoModalType = {
  show: boolean;
  text: string;
  handleClose: () => void;
};

function TodoModal({ show, text, handleClose }: TodoModalType): JSX.Element {
  return (
    <>
      <Modal show={show} onHide={handleClose} centered>
        <Modal.Header closeButton>
          <Modal.Title>Todo 상세 정보</Modal.Title>
        </Modal.Header>
        <Modal.Body>{text}</Modal.Body>
        <Modal.Footer>
          <Button variant="secondary" onClick={handleClose}>
            닫기
          </Button>
          <Button variant="primary" onClick={handleClose}>
            확인
          </Button>
        </Modal.Footer>
      </Modal>
    </>
  );
}

export default TodoModal;

  • ReactBootstrap 을 사용하여, Modal을 구성
  • show에 대한 여부를 상위 컴포넌트인 TodoListItem.tsx 에서 전달받아 통제 된다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.