From 493446fd2fe255308472bc7735297d7f304b6781 Mon Sep 17 00:00:00 2001 From: ctaaag Date: Fri, 12 May 2023 20:36:08 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EA=B3=BC=EC=A0=9C2=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 11 +++----- src/App.jsx | 37 ++++++++++++++++++++++++++ src/components/HandlerCreateButton.jsx | 0 src/components/HandlerDeleteButton.jsx | 0 src/components/Header.jsx | 5 ++++ src/components/TodoInput.jsx | 10 +++++++ src/components/TodoLists.jsx | 16 +++++++++++ src/index.jsx | 5 ++++ 8 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 src/App.jsx create mode 100644 src/components/HandlerCreateButton.jsx create mode 100644 src/components/HandlerDeleteButton.jsx create mode 100644 src/components/Header.jsx create mode 100644 src/components/TodoInput.jsx create mode 100644 src/components/TodoLists.jsx diff --git a/.eslintrc.js b/.eslintrc.js index 9f325a7d..fb8cd3df 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,10 +3,7 @@ module.exports = { browser: true, es2021: true, }, - extends: [ - 'plugin:react/recommended', - 'airbnb', - ], + extends: ['plugin:react/recommended', 'airbnb'], parserOptions: { ecmaFeatures: { jsx: true, @@ -14,9 +11,7 @@ module.exports = { ecmaVersion: 12, sourceType: 'module', }, - plugins: [ - 'react', - ], + plugins: ['react'], globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly', @@ -45,7 +40,7 @@ module.exports = { 'object-curly-spacing': ['error', 'always'], 'key-spacing': ['error', { mode: 'strict' }], 'arrow-spacing': ['error', { before: true, after: true }], - + 'react/jsx-filename-extension': ['warn', { extensions: ['.js', '.jsx'] }], 'react/prop-types': 'off', 'react/react-in-jsx-scope': 'off', }, diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 00000000..c7d1f624 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { useState } from 'react'; +import Header from './components/Header'; +import TodoInput from './components/TodoInput'; +import TodoLists from './components/TodoLists'; + +export default function App() { + const [inputText, setInputText] = useState(''); + const [todos, setTodos] = useState([]); + const randomId = () => Math.random().toString(12).substr(2, 16); + + const handleSubmit = (e) => { + e.preventDefault(); + setTodos([...todos, { id: randomId(), text: inputText }]); + setInputText(''); + }; + + const handleChange = (e) => { + setInputText(e.target.value); + }; + + const handleClickComplete = (e) => { + setTodos(todos.filter((item) => e.target.id !== item.id)); + }; + + return ( +
+
+ + +
+ ); +} diff --git a/src/components/HandlerCreateButton.jsx b/src/components/HandlerCreateButton.jsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/HandlerDeleteButton.jsx b/src/components/HandlerDeleteButton.jsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 00000000..5081a29c --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export default function Header() { + return
TO-DO
; +} diff --git a/src/components/TodoInput.jsx b/src/components/TodoInput.jsx new file mode 100644 index 00000000..4fc61e32 --- /dev/null +++ b/src/components/TodoInput.jsx @@ -0,0 +1,10 @@ +import React from 'react'; + +export default function TodoInput({ inputText, onSubmit, onChange }) { + return ( +
+ + +
+ ); +} diff --git a/src/components/TodoLists.jsx b/src/components/TodoLists.jsx new file mode 100644 index 00000000..46cd09ac --- /dev/null +++ b/src/components/TodoLists.jsx @@ -0,0 +1,16 @@ +import React from 'react'; + +export default function TodoLists({ todos, onClickComplete }) { + return ( +
    + {todos?.map((item) => ( +
  1. + {item.text} + +
  2. + ))} +
+ ); +} diff --git a/src/index.jsx b/src/index.jsx index e69de29b..a22f1759 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -0,0 +1,5 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; + +ReactDOM.render(, document.getElementById('app')); From db4b5f32877b1dbdd5c68099e9a6094307a315bc Mon Sep 17 00:00:00 2001 From: ctaaag Date: Fri, 12 May 2023 20:38:05 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20=EB=A6=B0=ED=8A=B8=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 1 + src/App.jsx | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index fb8cd3df..35e7f9a2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -43,5 +43,6 @@ module.exports = { 'react/jsx-filename-extension': ['warn', { extensions: ['.js', '.jsx'] }], 'react/prop-types': 'off', 'react/react-in-jsx-scope': 'off', + 'import/no-unresolved': 'off', }, }; diff --git a/src/App.jsx b/src/App.jsx index c7d1f624..0e077e31 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,4 @@ -import React from 'react'; -import { useState } from 'react'; +import React, { useState } from 'react'; import Header from './components/Header'; import TodoInput from './components/TodoInput'; import TodoLists from './components/TodoLists'; From 515cc000e0b66d42a74d6b549546a4b5e75a3ecb Mon Sep 17 00:00:00 2001 From: ctaaag Date: Fri, 12 May 2023 21:00:28 +0900 Subject: [PATCH 3/6] =?UTF-8?q?fix:=20=ED=95=A0=EC=9D=BC=EC=9D=B4=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=20=EB=95=8C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/TodoLists.jsx | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/components/TodoLists.jsx b/src/components/TodoLists.jsx index 46cd09ac..c8f068ad 100644 --- a/src/components/TodoLists.jsx +++ b/src/components/TodoLists.jsx @@ -1,16 +1,19 @@ import React from 'react'; export default function TodoLists({ todos, onClickComplete }) { - return ( -
    - {todos?.map((item) => ( -
  1. - {item.text} - -
  2. - ))} -
- ); + const showTodos = () => { + if (todos.length === 0) { + return 할 일이 없어요!; + } + return todos.map((item) => ( +
  • + {item.text} + +
  • + )); + }; + + return
      {showTodos()}
    ; } From 2e8abc687160d1772b231707b552897fce8fd310 Mon Sep 17 00:00:00 2001 From: ctaaag Date: Fri, 12 May 2023 21:03:02 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix=20:=20=EC=95=88=EC=93=B0=EB=8A=94=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/HandlerCreateButton.jsx | 0 src/components/HandlerDeleteButton.jsx | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/components/HandlerCreateButton.jsx delete mode 100644 src/components/HandlerDeleteButton.jsx diff --git a/src/components/HandlerCreateButton.jsx b/src/components/HandlerCreateButton.jsx deleted file mode 100644 index e69de29b..00000000 diff --git a/src/components/HandlerDeleteButton.jsx b/src/components/HandlerDeleteButton.jsx deleted file mode 100644 index e69de29b..00000000 From a4dcfe46efc8a1e473b83956c6ca53d5f70a1328 Mon Sep 17 00:00:00 2001 From: ctaaag Date: Sat, 13 May 2023 20:53:18 +0900 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20nanoid=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 23 +++++++++++++++++++++++ package.json | 1 + src/App.jsx | 26 +++++++++++++++----------- src/components/NoneTodos.jsx | 5 +++++ src/components/ShowTodos.jsx | 16 ++++++++++++++++ src/components/TodoForm.jsx | 10 ++++++++++ src/components/TodoInput.jsx | 10 ---------- src/components/TodoLists.jsx | 28 ++++++++++++---------------- 8 files changed, 82 insertions(+), 37 deletions(-) create mode 100644 src/components/NoneTodos.jsx create mode 100644 src/components/ShowTodos.jsx create mode 100644 src/components/TodoForm.jsx delete mode 100644 src/components/TodoInput.jsx diff --git a/package-lock.json b/package-lock.json index d37005aa..f6778799 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "nanoid": "^4.0.2", "react": "^17.0.2", "react-dom": "^17.0.2" }, @@ -6496,6 +6497,23 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "node_modules/nanoid": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", + "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^14 || ^16 || >=18" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -15040,6 +15058,11 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "nanoid": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", + "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==" + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", diff --git a/package.json b/package.json index 56170709..0bf39b21 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "webpack-dev-server": "^4.8.1" }, "dependencies": { + "nanoid": "^4.0.2", "react": "^17.0.2", "react-dom": "^17.0.2" } diff --git a/src/App.jsx b/src/App.jsx index 0e077e31..b11d966f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,36 +1,40 @@ import React, { useState } from 'react'; +import { nanoid } from 'nanoid'; import Header from './components/Header'; -import TodoInput from './components/TodoInput'; +import TodoForm from './components/TodoForm'; import TodoLists from './components/TodoLists'; export default function App() { - const [inputText, setInputText] = useState(''); + const [newTodo, setNewTodo] = useState(''); const [todos, setTodos] = useState([]); - const randomId = () => Math.random().toString(12).substr(2, 16); const handleSubmit = (e) => { e.preventDefault(); - setTodos([...todos, { id: randomId(), text: inputText }]); - setInputText(''); + setTodos([...todos, { id: nanoid(6), text: newTodo }]); + setNewTodo(''); }; const handleChange = (e) => { - setInputText(e.target.value); + setNewTodo(e.target.value); }; - const handleClickComplete = (e) => { - setTodos(todos.filter((item) => e.target.id !== item.id)); + const handleClickComplete = (clickedItemID) => { + setTodos(todos.filter((item) => item.id !== clickedItemID)); }; return (
    - - +
    ); } diff --git a/src/components/NoneTodos.jsx b/src/components/NoneTodos.jsx new file mode 100644 index 00000000..750de5b9 --- /dev/null +++ b/src/components/NoneTodos.jsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export default function NoneTodos() { + return 할 일이 없어요!; +} diff --git a/src/components/ShowTodos.jsx b/src/components/ShowTodos.jsx new file mode 100644 index 00000000..7277a79b --- /dev/null +++ b/src/components/ShowTodos.jsx @@ -0,0 +1,16 @@ +import React from 'react'; + +export default function ShowTodos({ todos, onClickComplete }) { + return ( +
      + {todos.map(({ id, text }) => ( +
    1. + {text} + +
    2. + ))} +
    + ); +} diff --git a/src/components/TodoForm.jsx b/src/components/TodoForm.jsx new file mode 100644 index 00000000..226df77c --- /dev/null +++ b/src/components/TodoForm.jsx @@ -0,0 +1,10 @@ +import React from 'react'; + +export default function TodoForm({ newTodo, onSubmit, onChange }) { + return ( +
    + + +
    + ); +} diff --git a/src/components/TodoInput.jsx b/src/components/TodoInput.jsx deleted file mode 100644 index 4fc61e32..00000000 --- a/src/components/TodoInput.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; - -export default function TodoInput({ inputText, onSubmit, onChange }) { - return ( -
    - - -
    - ); -} diff --git a/src/components/TodoLists.jsx b/src/components/TodoLists.jsx index c8f068ad..82457868 100644 --- a/src/components/TodoLists.jsx +++ b/src/components/TodoLists.jsx @@ -1,19 +1,15 @@ import React from 'react'; +import NoneTodos from './NoneTodos'; +import ShowTodos from './ShowTodos'; -export default function TodoLists({ todos, onClickComplete }) { - const showTodos = () => { - if (todos.length === 0) { - return 할 일이 없어요!; - } - return todos.map((item) => ( -
  • - {item.text} - -
  • - )); - }; - - return
      {showTodos()}
    ; +export default function TodoLists({ hasTodos, todos, onClickComplete }) { + return ( + <> + {hasTodos ? ( + + ) : ( + + )} + + ); } From 0fd3deac4e6eb03ddd7467a53bca78c6104ac71b Mon Sep 17 00:00:00 2001 From: ctaaag Date: Sun, 14 May 2023 20:15:22 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix=20:=20newTodo=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD,=20TodoLists=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B0=80=EB=93=9C=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 16 ++++++---------- src/components/TodoForm.jsx | 4 ++-- src/components/TodoLists.jsx | 17 +++++++---------- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index b11d966f..69869744 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -5,17 +5,17 @@ import TodoForm from './components/TodoForm'; import TodoLists from './components/TodoLists'; export default function App() { - const [newTodo, setNewTodo] = useState(''); + const [newOrUpdateTodo, setNewOrUpdateTodo] = useState(''); const [todos, setTodos] = useState([]); const handleSubmit = (e) => { e.preventDefault(); - setTodos([...todos, { id: nanoid(6), text: newTodo }]); - setNewTodo(''); + setTodos([...todos, { id: nanoid(6), text: newOrUpdateTodo }]); + setNewOrUpdateTodo(''); }; const handleChange = (e) => { - setNewTodo(e.target.value); + setNewOrUpdateTodo(e.target.value); }; const handleClickComplete = (clickedItemID) => { @@ -26,15 +26,11 @@ export default function App() {
    - +
    ); } diff --git a/src/components/TodoForm.jsx b/src/components/TodoForm.jsx index 226df77c..9847045c 100644 --- a/src/components/TodoForm.jsx +++ b/src/components/TodoForm.jsx @@ -1,9 +1,9 @@ import React from 'react'; -export default function TodoForm({ newTodo, onSubmit, onChange }) { +export default function TodoForm({ newOrUpdateTodo, onSubmit, onChange }) { return (
    - +
    ); diff --git a/src/components/TodoLists.jsx b/src/components/TodoLists.jsx index 82457868..8de65e7a 100644 --- a/src/components/TodoLists.jsx +++ b/src/components/TodoLists.jsx @@ -2,14 +2,11 @@ import React from 'react'; import NoneTodos from './NoneTodos'; import ShowTodos from './ShowTodos'; -export default function TodoLists({ hasTodos, todos, onClickComplete }) { - return ( - <> - {hasTodos ? ( - - ) : ( - - )} - - ); +const isEmpty = (arr = []) => arr.length === 0; + +export default function TodoLists({ todos, onClickComplete }) { + if (isEmpty(todos)) { + return ; + } + return ; }