From 6ad23641172e36bc3ea695541cfe90075112a275 Mon Sep 17 00:00:00 2001 From: soyeeee2 Date: Mon, 10 Jun 2024 01:06:13 +0900 Subject: [PATCH 1/7] =?UTF-8?q?ui=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ui/Footer.jsx | 25 +++++++++++++++++++++++++ src/components/ui/SubmitForm.jsx | 29 +++++++++++++++++++++++++++++ src/components/ui/TodoList.jsx | 31 +++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 src/components/ui/Footer.jsx create mode 100644 src/components/ui/SubmitForm.jsx create mode 100644 src/components/ui/TodoList.jsx diff --git a/src/components/ui/Footer.jsx b/src/components/ui/Footer.jsx new file mode 100644 index 00000000..dd02a175 --- /dev/null +++ b/src/components/ui/Footer.jsx @@ -0,0 +1,25 @@ +import React from "react"; +import './main.module.css'; + +const Footer = ({lists, ShowCompletedToggle, ClearCompleted}) => { + return ( +
  • +
    +

    {lists.length}

    +

    items left

    +
    +
    + + + +
    +
    + +
    +
  • + ); +}; + +export default Footer; diff --git a/src/components/ui/SubmitForm.jsx b/src/components/ui/SubmitForm.jsx new file mode 100644 index 00000000..b8e9455d --- /dev/null +++ b/src/components/ui/SubmitForm.jsx @@ -0,0 +1,29 @@ +import React, { useRef } from "react"; +// import HandleSubmit from "../feat/HandleSubmit"; + +const SubmitForm = ({e, onSubmit, inputRef, lists, setLists}) => { + + return ( +
    +
    +
    + + +
    + +
    +
    + ); +}; + +export default SubmitForm; diff --git a/src/components/ui/TodoList.jsx b/src/components/ui/TodoList.jsx new file mode 100644 index 00000000..afe8f96e --- /dev/null +++ b/src/components/ui/TodoList.jsx @@ -0,0 +1,31 @@ +import React from "react"; +import HandleDeleteItem from "../handlers/HandleDeleteItem"; +import HandleToggleComplete from "../handlers/HandleToggleComplete"; + +const TodoList = ({ lists, HandleToggleComplete, HandleDeleteItem}) => { + return ( + + ); +}; + +export default TodoList; From 81b0e51f2a9f575269ce277f36691925e8335a6c Mon Sep 17 00:00:00 2001 From: soyeeee2 Date: Mon, 10 Jun 2024 01:06:44 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=EA=B8=B0=EB=8A=A5=20=EC=9E=91?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/handlers/ClearCompleted.js | 35 ++++++++++++++++++ src/components/handlers/HandleDeleteItem.js | 8 ++++ src/components/handlers/HandleSubmit.js | 18 +++++++++ .../handlers/HandleToggleComplete.js | 35 ++++++++++++++++++ .../handlers/ShowCompletedToggle.js | 37 +++++++++++++++++++ 5 files changed, 133 insertions(+) create mode 100644 src/components/handlers/ClearCompleted.js create mode 100644 src/components/handlers/HandleDeleteItem.js create mode 100644 src/components/handlers/HandleSubmit.js create mode 100644 src/components/handlers/HandleToggleComplete.js create mode 100644 src/components/handlers/ShowCompletedToggle.js diff --git a/src/components/handlers/ClearCompleted.js b/src/components/handlers/ClearCompleted.js new file mode 100644 index 00000000..143d14e6 --- /dev/null +++ b/src/components/handlers/ClearCompleted.js @@ -0,0 +1,35 @@ +import React from "react"; + +const ClearCompleted = (lists, setLists) => { + setLists((prevLists) => { + // 기존 localStorage 데이터를 불러옵니다. + const items = []; + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + const value = JSON.parse(localStorage.getItem(key)); + items.push({ + id: value.id, + text: value.text, + completed: value.completed, + }); + } + + // 완료된 항목을 필터링합니다. + const completedItems = items.filter((item) => item.completed); + const newStoredLists = items.filter((item) => !item.completed); + + // localStorage에서 완료된 항목을 제거합니다. + completedItems.forEach((item) => { + localStorage.removeItem(item.text); + }); + + // localStorage에 업데이트된 목록을 저장합니다. + newStoredLists.forEach((item) => { + localStorage.setItem(item.text, JSON.stringify(item)); + }); + + return newStoredLists; + }); +}; + +export default ClearCompleted; diff --git a/src/components/handlers/HandleDeleteItem.js b/src/components/handlers/HandleDeleteItem.js new file mode 100644 index 00000000..2528e219 --- /dev/null +++ b/src/components/handlers/HandleDeleteItem.js @@ -0,0 +1,8 @@ +import React from "react"; + +const HandleDeleteItem = (id, lists, setLists) => { + const filteredItems = lists.filter((item) => item.id !== id); + setLists(filteredItems); +}; + +export default HandleDeleteItem; diff --git a/src/components/handlers/HandleSubmit.js b/src/components/handlers/HandleSubmit.js new file mode 100644 index 00000000..18ec7009 --- /dev/null +++ b/src/components/handlers/HandleSubmit.js @@ -0,0 +1,18 @@ +const HandleSubmit = (e, NewID, inputRef, lists, setLists) => { + e.preventDefault(); + + const newTodo = inputRef.current.value; + if (!newTodo.trim()) return; // Avoid empty submissions + + const todoObject = { id: NewID(), text: newTodo, completed: false }; + + localStorage.setItem(newTodo, JSON.stringify(todoObject)); + + const storageItem = localStorage.getItem(newTodo); + const newItem = JSON.parse(storageItem); + + setLists([...lists, newItem]); + inputRef.current.value = ""; +}; + +export default HandleSubmit; diff --git a/src/components/handlers/HandleToggleComplete.js b/src/components/handlers/HandleToggleComplete.js new file mode 100644 index 00000000..1a8dda68 --- /dev/null +++ b/src/components/handlers/HandleToggleComplete.js @@ -0,0 +1,35 @@ +import React from "react"; + +const HandleToggleComplete = (id, lists, setLists) => { + setLists((prevLists) => { + const updatedLists = prevLists.map((item) => + item.id === id ? { ...item, completed: !item.completed } : item + ); + + // 기존 localStorage 데이터를 불러옵니다. + const items = []; + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + const value = JSON.parse(localStorage.getItem(key)); + items.push({ + id: value.id, + text: value.text, + completed: value.completed, + }); + } + + // 업데이트된 항목을 반영합니다. + const newStoredLists = items.map((item) => + item.id === id ? { ...item, completed: !item.completed } : item + ); + + // localStorage에 업데이트된 목록을 저장합니다. + newStoredLists.forEach((item) => { + localStorage.setItem(item.text, JSON.stringify(item)); + }); + + return updatedLists; + }); +}; + +export default HandleToggleComplete; diff --git a/src/components/handlers/ShowCompletedToggle.js b/src/components/handlers/ShowCompletedToggle.js new file mode 100644 index 00000000..bdb5382b --- /dev/null +++ b/src/components/handlers/ShowCompletedToggle.js @@ -0,0 +1,37 @@ +import React from "react"; + +const ShowCompletedToggle = (condition, lists, setLists) => { + //condition따라 toggle filter + // localStorage.getItem() + + setLists((prevLists) => { + // 기존 localStorage 데이터를 불러옵니다. + const items = []; + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + const value = JSON.parse(localStorage.getItem(key)); + items.push({ + id: value.id, + text: value.text, + completed: value.completed, + }); + } + + // 업데이트된 항목을 반영합니다. + const newStoredLists = + condition === "active" + ? items.filter((item) => !item.completed) + : condition === "completed" + ? items.filter((item) => item.completed) + : items; + + // localStorage에 업데이트된 목록을 저장합니다. + newStoredLists.forEach((item) => { + localStorage.setItem(item.text, JSON.stringify(item)); + }); + + return newStoredLists; + }); +}; + +export default ShowCompletedToggle; From a0d4db703e25377766b8a6c2536e50438bd474e1 Mon Sep 17 00:00:00 2001 From: soyeeee2 Date: Mon, 10 Jun 2024 01:07:19 +0900 Subject: [PATCH 3/7] =?UTF-8?q?setting:=20=EC=84=B8=ED=8C=85=20=EC=9E=AC?= =?UTF-8?q?=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 2 +- src/App.js | 6 ++++++ src/Main.jsx | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/App.js create mode 100644 src/Main.jsx diff --git a/index.html b/index.html index b021b5c8..fa9a2743 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,6 @@
    - + diff --git a/src/App.js b/src/App.js new file mode 100644 index 00000000..bfbdc828 --- /dev/null +++ b/src/App.js @@ -0,0 +1,6 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import Main from "./Main"; + +const root = ReactDOM.createRoot(document.getElementById("app")); +root.render(React.createElement(Main)); diff --git a/src/Main.jsx b/src/Main.jsx new file mode 100644 index 00000000..966fc890 --- /dev/null +++ b/src/Main.jsx @@ -0,0 +1,26 @@ +import "./App.css"; +import React from "react"; +import useTodoState from "./components/hooks/useTodoState"; +import SubmitForm from "./components/ui/SubmitForm"; +import TodoList from "./components/ui/TodoList"; +import Footer from "./components/ui/Footer"; + +function Main() { + window.addEventListener('load', () => { + localStorage.clear(); + }); + + const { lists, setLists, inputRef, onSubmit, onDelete, onToggle, showConditionToggle, filteredTodoList } = useTodoState(); + + return ( +
    +
    + + +
    +
    +
    + ); +} + +export default Main; From 1e3aca80e45b5c0d151fcb1ed474cd2fc9c4482f Mon Sep 17 00:00:00 2001 From: soyeeee2 Date: Mon, 10 Jun 2024 01:07:34 +0900 Subject: [PATCH 4/7] feat: hooks update --- src/components/hooks/useTodoState.js | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/components/hooks/useTodoState.js diff --git a/src/components/hooks/useTodoState.js b/src/components/hooks/useTodoState.js new file mode 100644 index 00000000..d3d58441 --- /dev/null +++ b/src/components/hooks/useTodoState.js @@ -0,0 +1,33 @@ +import { useState, useRef } from "react"; +import HandleSubmit from "../handlers/HandleSubmit"; +import HandleDeleteItem from "../handlers/HandleDeleteItem"; +import HandleToggleComplete from "../handlers/HandleToggleComplete"; +import ClearCompleted from "../handlers/ClearCompleted"; +import ShowCompletedToggle from "../handlers/ShowCompletedToggle"; + +const useTodoState = () => { + const [lists, setLists] = useState([]); + const NewID = () => Math.random().toString(36).substr(2, 16); + const inputRef = useRef(); + + const onSubmit = (e) => HandleSubmit(e, NewID, inputRef, lists, setLists); + const onDelete = (id) => HandleDeleteItem(id, lists, setLists); + const onToggle = (id) => HandleToggleComplete(id, lists, setLists, NewID); + const showConditionToggle = (condition) => + ShowCompletedToggle(condition, lists, setLists); + const filteredTodoList = () => ClearCompleted(lists, setLists); + + return { + lists, + setLists, + NewID, + inputRef, + onSubmit, + onDelete, + onToggle, + filteredTodoList, + showConditionToggle, + }; +}; + +export default useTodoState; From 36c3d68f75ef32c0448a7002ed9fbac14dd8542a Mon Sep 17 00:00:00 2001 From: soyeeee2 Date: Mon, 10 Jun 2024 01:38:03 +0900 Subject: [PATCH 5/7] =?UTF-8?q?README=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c0710d2..d8dba8c3 100644 --- a/README.md +++ b/README.md @@ -1 +1,68 @@ -# react-todo-list-precourse \ No newline at end of file +# react-todo-list-precourse + +# Setting +## App.js +- root를 생성해 Main을 로드합니다. + +## Main.jsx +- 생성됨과 동시에 hooks를 로드합니다 by. useTodoState +- UI에 state와 기능들을 전달합니다. + + + + +# UI +## SubmitForm +- user에게 submit을 받는 input을 포함한 UI입니다. +- hooks에서 받아온 onSubmit으로 사용자의 submit을 감지하고 item을 추가해 state를 변경하도록 합니다. + +## TodoList +- user가 입력한 todo를 화면에 그려줍니다. +- 앞의 체크박스를 체크하면 item의 completed 부분이 true로 변하며 state와 localstorage가 변경됩니다. +- hooks에서 받아온 HandleToggleComplete로 이벤트를 감지하여 style도 취소선으로 변경됩니다. + +## Footer +- lists를 확인하고 남은 개수를 보여줍니다. +- hooks에서 받아온 ShowCompletedToggle로 아래의 버튼을 눌렀을 시, condition(all, active, completed)에 따라 해당하는 아이템들만 보여주도록 했습니다. +- Clear Completed 버튼을 눌렀을 시, hooks에서 받아온 ClearCompleted로 state와 localstorage를 변경하였습니다. + + + + +# 기능 +- Hooks를 통해 State를 관리하고 handlers에 부여해주는 방식으로 구현하였습니다. + +## Hooks +### useTodoState +- 전체 아이템들을 관장하는 lists state를 관리합니다. +- item에 랜덤으로 부여되는 NewID를 가지고 있습니다. +- inputRef로 DOM을 관리합니다. +- handlers에게 state나 변수 기능들을 인수로 전달합니다. + +## handlers +### HandleSubmit +- submit시 newTodo를 생성하고 input의 value를 받아 todoObject 형식으로 만들어 state와 localstorage에 전달합니다. +- 이후 SubmitForm을 다시 초기화 합니다. + +### HandleDelete +- 아이템을 지웠을 시, filter를 거쳐 id와 일치하는 아이템을 지우고 state와 localstorage를 업데이트 합니다. + +### HandleToggleComplete +- item 빈배열을 만들고 localstorage의 모든 아이템을 가져와 상태를 확인합니다. +- 클릭된(completed) 아이템만 취소선을 그어줍니다. + +### ShowCompletedToggle +- Footer의 버튼들을 클릭하면 해당하는 condition을 받아옵니다. +- condition(all, active, completed)에 따라 보여줄 아이템들을 필터링합니다. +- 필터링한 아이템을 state와 localstorage에 반영합니다. + +### ClearCompleted +- 완료된 항목들을 필터링합니다. +- 필터링한(clear)된 항목들을 state에 반영하고 localstorage에서 제거합니다. + + + + + +# 느낀점 +- module로 분리해서 작업하다보니 state 관리로 애를 많이 먹었습니다. 다른 쿠키즈들의 코드를 보면서 공부하여 hooks 라는 새로운 방식이 있음을 알게 되고 활용하여 한 곳에서 state와 DOM, 기능들을 관리하는 법을 배운 것 같아 얻어가는게 많은 과제였습니다. From 47583d4bd3b840d4b2031ed138719a977c226fd3 Mon Sep 17 00:00:00 2001 From: soyeeee2 Date: Mon, 10 Jun 2024 02:09:55 +0900 Subject: [PATCH 6/7] =?UTF-8?q?fix:=20style=20=EB=84=A4=EC=9D=B4=EB=B0=8D?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Main.jsx | 4 ++-- src/components/ui/Footer.jsx | 12 ++++++------ src/components/ui/SubmitForm.jsx | 14 +++++++------- src/components/ui/TodoList.jsx | 11 ++++++----- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/Main.jsx b/src/Main.jsx index 966fc890..79e28d2f 100644 --- a/src/Main.jsx +++ b/src/Main.jsx @@ -6,7 +6,7 @@ import TodoList from "./components/ui/TodoList"; import Footer from "./components/ui/Footer"; function Main() { - window.addEventListener('load', () => { + window.addEventListener("load", () => { localStorage.clear(); }); @@ -14,7 +14,7 @@ function Main() { return (
    -
    +