React로 To-Do List 만들기
제목과 내용을 입력하고 [추가하기] 버튼을 클릭 시, 새로운 Todo가 Working에 추가되고,
제목/내용 입력창 내부는 빈 창으로 초기화
Todo의 상태가 Working이면 위쪽에 위치하고, Done이면 아래쪽에 위치시킴
Todo의 isDone 상태가 true면 상태 버튼의 라벨을 [취소]로 설정하고,
Todo의 isDone 상태가 false면 상태 버튼의 라벨을 [완료]로 설정
[삭제하기] 버튼 클릭 시, 해당 Todo를 삭제
Layout의 최대 너비는 1200px, 최소 너비는 800px로 설정하고, 전체 화면의 가운데로 정렬
반복되는 컴포넌트는 분리
// App.jsx
function App() {
const [todoList, setTodoList] = useState([
{
id: 1,
title: "리액트 공부하기",
content: "생각보다는 쉽지만 그래도 어려워요ㅠㅠ",
isDone: false,
},
{
id: 2,
title: "휴식하기",
content: "그런건 없음ㅠㅠ",
isDone: true,
}
]);
return (
<div className="layout">
</div>
)
}
export default App;
/* App.css */
.layout {
min-width: 800px;
max-width: 1200px;
margin: 0 auto;
}
// App.jsx
return (
<div className="header">
<h3>My Todo List</h3>
<h3>React</h3>
</div>
);
/* App.css */
.header {
display: flex;
justify-content: space-between;
padding: 0 20px;
}
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const titleChangeHandler = (event) => {
setTitle(event.target.value);
};
const contentChangeHandler = (event) => {
setContent(event.target.value);
};
const addTodo = () => {
const newTodo = {
id: todoList.length + 1,
title,
content,
isDone: false,
};
setTodoList([...todoList, newTodo]);
};
// App.jsx
return (
<div className="add-todo">
<div className="todo-info">
<label className="form-title">제목</label>
<input
className="form-content"
name="todo-title"
value={title}
onChange={titleChangeHandler}
/>
<label className="form-title">내용</label>
<input
className="form-content"
name="todo-content"
value={content}
onChange={contentChangeHandler}
/>
</div>
<button className="addBtn" onClick={addTodo}>
추가하기
</button>
</div>
);
/* App.css */
.add-todo {
display: flex;
justify-content: space-between;
padding: 20px;
background-cooor: whitesmoke;
}
.form-title {
font-weight: bold;
}
.form-content {
width: 200px;
margin: 0 20px;
padding: 10px;
border: none;
border-rarius: 10 px;
}
.addBtn {
width: 100px;
border: none;
border-radius: 10px;
background-color: powderblue;
font-weight: bold;
}
filter() 메서드를 사용하여 선택한 todo의 id와 일치하지 않는 todo를 새 todoList로 설정
const deletetodo = (id) => {
const newTodoList = todoList.filter((todo) => todo.id !== id);
setTodoList(newTodoList);
};
map() 메서드를 사용하여 선택한 todo의 isDone 상태를 변경함
const checkTodoDone = (id) => {
const newTodoList = todoList.map((todo) => {
if(todo.id === id) {
return {...todo, isDone: !todo.isDone};
} else {
return {...todo};
}
});
setTodoList(newTodoList);
};
// App.jsx
return (
<div className="todo-list">
<div className="list-container">
<h2>Working..</h2>
<div className="working-list">
{todoList
.filter((todo) => !todo.isDone)
.map((todo) => {
return (
<div key={todo.id} className="todo-item">
<h3>{todo.title}</h3>
<p>{todo.content]<p>
<button className="removeBtn" onClick={() => deleteTodo(todo.id)}>
삭제
</button>
<button className="checkDoneBtn" onClick={() => checkTodoDone(todo.id)}>
{todo.isDone ? "취소" : "완료"}
</button>
</div>
);
})
}
</div>
</div>
<div className="list-container">
<h2>Done!!</h2>
<div className="done-list">
{todoList
.filter((todo) => todo.isDone)
.map((todo) => {
return (
<div key={todo.id} className="todo-item">
<h3>{todo.title}</h3>
<p>{todo.content]<p>
<button className="removeBtn" onClick={() => deleteTodo(todo.id)}>
삭제
</button>
<button className="checkDoneBtn" onClick={() => checkTodoDone(todo.id)}>
{todo.isDone ? "취소" : "완료"}
</button>
</div>
);
})
}
</div>
</div>
</div>
);
/* App.css */
.todo-list {
padding: 20px;
}
.todo-item {
margin: 20px;
padding: 20px;
border: 3px solid darkseagreen;
border-radius: 10px;
}
.removeBtn,
.checkDoneBtn {
width: 50px;
height: 30px;
margin-right: 10px;
}
.removeBtn {
background-color: white;
border: 3px solid red;
border-radius: 10px;
}
.checkDoneBtn {
background-color: white;
border: 3px solid green;
border-radius: 10px;
}