Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,33 @@
# react-todo-list-precourse
# react-todo-list-precourse

## 할 일 목록

하루 또는 한 주의 할 일 목록을 업데이트하는 할 일 목록을 구현한다.

### 기능 설명

- 사용자는 할 일을 입력한다.
- 추가 버튼 클릭, 혹은 엔터 키 클릭으로 할 일을 추가한다.(아무것도 입력하지 않았을 경우 추가 x)
- 사용자가 추가한 할 일의 목록을 볼 수 있다.
- 할 일의 완료 상태를 전활할 수 있다.

### 선택 기능 설명

- 현재 진행 중인 할 일, 완료된 할 일, 모든 할 일을 필터링할 수 있다.
- 해야 할 일의 총개수를 확인할 수 있다.
- 새로고침을 하여도 이전에 작성한 데이터는 유지되어야 한다.

### 에러 처리

사용자가 아무것도 입력하지 않았을 경우<br/>
-> 추가 버튼 클릭 비활성화 및 엔터 키 비활성화

### 기술 스택

HTML, CSS, JavaScript, React

### 컴포넌트 분리

- 할 일 입력
- 할 일 목록
- 필터링 버튼
23 changes: 13 additions & 10 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/App.js"></script>
</body>

</html>
6 changes: 6 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from "react";
import { createRoot } from "react-dom/client";
import Main from "./main";

const root = createRoot(document.getElementById("root"));
root.render(React.createElement(Main));
8 changes: 8 additions & 0 deletions src/components/newTodo/ButtonSubmit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useCallback } from 'react';

export const onButtonSubmitHandler = (texting, setNewTodo, setTexting) => useCallback(() => {
if (texting) {
setNewTodo(texting);
setTexting('');
}
}, [texting, setNewTodo]);
5 changes: 5 additions & 0 deletions src/components/newTodo/ChangeTexting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useCallback } from 'react';

export const onChangeText = (setTexting) => useCallback((e) => {
setTexting(e.target.value);
}, []);
8 changes: 8 additions & 0 deletions src/components/newTodo/EnterSubmit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useCallback } from 'react';

export const onEnterSubmitHandler = (texting, setNewTodo, setTexting) => useCallback((e) => {
if (e.key === 'Enter' && texting) {
setNewTodo(texting);
setTexting('');
}
}, [texting, setNewTodo]);
16 changes: 16 additions & 0 deletions src/components/newTodo/NewTodo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import './newTodo.styles.css';
import { Texting } from './Texting';

const NewTodo = ({ setNewTodo }) => {
const { texting, onChangeText, onEnterSubmitHandler, onButtonSubmitHandler } = Texting(setNewTodo);

return (
<div className="newtodo">
<input type="text" value={texting} placeholder="What needs to be done?" onChange={onChangeText} onKeyDown={onEnterSubmitHandler} />
{texting && <button onClick={onButtonSubmitHandler}>ADD</button>}
</div>
);
}

export default NewTodo;
14 changes: 14 additions & 0 deletions src/components/newTodo/Texting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useState } from 'react';
import { onChangeText } from './ChangeTexting';
import { onEnterSubmitHandler } from './EnterSubmit';
import { onButtonSubmitHandler } from './ButtonSubmit';

export const Texting = (setNewTodo) => {
const [texting, setTexting] = useState('');

const changeText = onChangeText(setTexting);
const enterSubmitHandler = onEnterSubmitHandler(texting, setNewTodo, setTexting);
const buttonSubmitHandler = onButtonSubmitHandler(texting, setNewTodo, setTexting);

return { texting, onChangeText: changeText, onEnterSubmitHandler: enterSubmitHandler, onButtonSubmitHandler: buttonSubmitHandler };
};
17 changes: 17 additions & 0 deletions src/components/newTodo/newTodo.styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.newtodo {
width: 100vw;
text-align: center;
height: 50px;
}

.newtodo input {
padding: 10px 20px;
font-size: 15px;
border: 1.7px solid rgb(167, 44, 54);
border-radius: 10px;
}

.newtodo button {
margin-left: 10px;
padding: 5px 10px;
}
3 changes: 3 additions & 0 deletions src/components/todoList/DeleteButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const onDeleteButtonHandler = (todoList, setTodoList, idx) => {
setTodoList(todoList.filter((_, index) => index !== idx));
};
26 changes: 26 additions & 0 deletions src/components/todoList/TodoList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { useState, useEffect } from "react";
import './todoList.styles.css';
import { toggleDone } from "./ToggleDone";
import { onDeleteButtonHandler } from "./DeleteButton";

const TodoList = ({ todoList, setTodoList }) => {
const [doneStatus, setDoneStatus] = useState(Array(todoList.length).fill(false));

useEffect(() => {
setDoneStatus(Array(todoList.length).fill(false));
}, [todoList]);

return (
<div className="todolist">
{todoList ? todoList.map((todo, idx) => (
<div className={`todolist-boxs${doneStatus[idx] ? '-done' : ''}`} key={idx}>
{todo}
<button onClick={() => toggleDone(doneStatus, setDoneStatus, idx)}>✓</button>
<button onClick={() => onDeleteButtonHandler(todoList, setTodoList, idx)}>X</button>
</div>
)) : <></>}
</div>
);
}

export default TodoList;
3 changes: 3 additions & 0 deletions src/components/todoList/ToggleDone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const toggleDone = (doneStatus, setDoneStatus, idx) => {
setDoneStatus(doneStatus.map((status, index) => idx === index ? !status : status));
};
25 changes: 25 additions & 0 deletions src/components/todoList/todoList.styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.todolist-boxs button {
margin-left: 10px;
}

.todolist-boxs-done button {
margin-left: 10px;
}

.todolist-boxs-done button:nth-child(1) {
color: #ccc;
}

.todolist-boxs {
padding: 10px;
border: 1px solid #ccc;
margin: 5px 0;
}

.todolist-boxs-done {
padding: 10px;
border: 1px solid #ccc;
margin: 5px 0;
text-decoration: line-through;
color: #ccc;
}
Empty file removed src/main.js
Empty file.
21 changes: 21 additions & 0 deletions src/main.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { useState, useEffect } from "react";
import NewTodo from "./components/newTodo/NewTodo";
import TodoList from "./components/todoList/TodoList";

const Main = () => {
const [newTodo, setNewTodo] = useState('');
const [todoList, setTodoList] = useState([]);
useEffect(() => {
newTodo ? setTodoList([...todoList, newTodo]) : '';
setNewTodo('');
}, [newTodo])
return (
<div className="app">
<h1>todos</h1>
<NewTodo newText={newTodo} setNewTodo={setNewTodo} />
<TodoList todoList={todoList} setTodoList={setTodoList} />
</div>
);
}

export default Main;