새로운 게시물 추가
입력창에서 엔터를 치면 바로 게시물이 추가되도록 form으로 구현했다.
import React, { useState } from 'react';
import { Button, Form, Container, Row, Col } from 'react-bootstrap';
import TodoModal from './TodoModal';
import './Todolist.css';
type Todo = {
id: number;
text: string;
isChecked: boolean;
};
const TodoList: React.FC = () => {
const title: string = '오늘 할 일';
const [todos, setTodos] = useState<Todo[]>([
{ id: 1, text: '공부하기', isChecked: false },
{ id: 2, text: '잠자기', isChecked: false },
{ id: 3, text: '미팅하기', isChecked: false }
]);
const [newTodo, setNewTodo] = useState<string>('');
...
const addTodo = (e: (React.FormEvent<HTMLFormElement> | null)) => {
e?.preventDefault();
if (newTodo.trim() !== '') {
setTodos([...todos, { id: Date.now(), text: newTodo, isChecked: false }]);
setNewTodo('');
}
}
return (
<div>
...
<Form onSubmit={(e) => addTodo(e)} className='my-4'>
<Row className="justify-content-center">
<Col xs={6} className='text-end'>
<Form.Control type="text"
placeholder="할 일 입력"
onChange={(e) => setNewTodo(e.target.value)} value={newTodo} />
</Col>
<Col xs='auto'>
<Button variant='primary' onClick={() => addTodo(null)}>추가</Button>
</Col>
</Row>
</Form>
...
</div>
)
}
export default TodoList;
시계 추가
setInterval 함수 안에서 setState를 사용하여 1초마다 시계가 업데이트되도록 설정했다.
const Clock: React.FC = () => {
const [time, setTime] = useState(new Date());
setInterval(() => {
setTime(new Date());
}, 1000)
return (
<div>
<h2 className="fs-5 fw-bold">현재 시간</h2>
<p className="fs-6">
{time.toLocaleTimeString()}
</p>
</div>
)
}
게시물 삭제하기
삭제 버튼 클릭시 해당 todo의 id를 todo 삭제 함수의 인수로 전달한다. todo 삭제 함수인 removeTodo에서는 todo 리스트 중 인수로 받은 id를 제외한 todo들만 저장한다.
const TodoList: React.FC = () => {
const title: string = '오늘 할 일';
const [todos, setTodos] = useState<Todo[]>([
{ id: 1, text: '공부하기', isChecked: false },
{ id: 2, text: '잠자기', isChecked: false },
{ id: 3, text: '미팅하기', isChecked: false }
]);
...
const removeTodo = (id: number) => {
setTodos(todos.filter(todo => todo.id !== id));
}
return (
<div>
...
<Container>
<Row className='board'>
<ul>
{
todos.map(todo => {
return (
<li key={todo.id}>
<Row className='align-items-center my-1'>
<Col xs={1} className='text-center'>
<Form.Check onChange={() => handleCheckedChange(todo.id)} />
</Col>
<Col xs={9} onClick={() => handleTodoClick(todo)}>
{
todo.isChecked ?
<del>{todo.text}</del> :
<span>{todo.text}</span>
}
</Col>
<Col xs={2} className='text-end'>
<Button variant='outline-danger' onClick={() => { removeTodo(todo.id) }}>삭제</Button>
</Col>
</Row>
</li>)
})
}
</ul>
</Row>
</Container >
</div>
)
}
export default TodoList;
props 사용
일기예보를 나타내기 위해 App 컴포넌트에서 다음과 같이 weather이라는 인수를 MyWeather 컴포넌트에 전달해야 한다.
function App() {
return (
<div className='container'>
...
<MyWeather weather='맑음'>일기예보</MyWeather>
...
</div>
);
}
MyWeather 컴포넌트에서는 다음과 같이 구조 분해를 이용하여 인자를 전달받을 수 있다. App 컴포넌트에서 MyWeather 태그 사이에 작성한 값이 children으로 들어온다.
interface MyProps {
weather: string;
children: React.ReactNode;
}
const MyWeather: React.FC<MyProps> = ({ children, weather }) => {
return (
<div>
<h2 className="fs-5 fw-bold">{children}</h2>
<p className="fs-6">
오늘의 날씨는 {weather} 입니다.
</p>
</div>
)
}
모달 대화상자 추가
todo를 클릭하면 모달 대화상자가 뜨도록 해보자.
todo를 클릭하면 모달 대화상자의 표시 여부인 showDetail을 true로 설정한다. 그리고 어떤 todo의 상세정보를 보여줄지 결정해야 하기 때문에 selectedTodo를 클릭된 todo로 설정한다.
모달 컴포넌트에서 showDetail을 false로 설정하여 모달을 닫을 수 있도록 handleCloseDetail 함수를 전달해준다.
const TodoList: React.FC = () => {
...
const [showDetail, setShowDetail] = useState<boolean>(false);
const [selectedTodo, setSelectedTodo] = useState<Todo | null>(null);
...
const handleTodoClick = (todo: Todo) => {
setShowDetail(true);
setSelectedTodo(todo);
}
const handleCloseDetail = () => {
setShowDetail(false);
}
return (
<div>
...
<TodoModal show={showDetail} todo={selectedTodo} handleClose={handleCloseDetail}></TodoModal>
</div>
)
}
show가 true면 모달 창을 띄운다. X 버튼이나 Close 버튼을 누르면 handleClose 함수를 통해 show가 false로 설정되어 모달 창이 닫힌다.
type Todo = {
id: number;
text: string;
isChecked: boolean;
};
type TodoModalProps = {
show: boolean;
todo: Todo | null;
handleClose: () => void;
}
const TodoModal: React.FC<TodoModalProps> = ({ show, todo, handleClose }) => {
return (
<div>
<Modal show={show} onHide={handleClose} centered>
<Modal.Header closeButton>
<Modal.Title>Todo 상세정보</Modal.Title>
</Modal.Header>
<Modal.Body>{todo?.text}</Modal.Body>
<Modal.Footer>
<Button onClick={handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</div>
)
}
전체 코드
https://github.com/ncherryu/TodoList/tree/main
'데브코스' 카테고리의 다른 글
[12주차 주간 발표] 인덱스 (0) | 2024.05.15 |
---|---|
[12주차 - DAY2] 게시판 만들기(1) (0) | 2024.05.14 |
[11주차 - DAY5] 리액트(2) TodoList 만들기 (1) | 2024.05.10 |
[11주차 - DAY4] 리액트(1) (0) | 2024.05.09 |
[11주차 주간 발표] const assertion과 RORO 패턴 (0) | 2024.05.09 |