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 라이센스를 따릅니다.