diff --git a/.eslintrc.js b/.eslintrc.js index 9f325a7d..35e7f9a2 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,8 +40,9 @@ 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', + 'import/no-unresolved': 'off', }, }; 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 new file mode 100644 index 00000000..69869744 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,36 @@ +import React, { useState } from 'react'; +import { nanoid } from 'nanoid'; +import Header from './components/Header'; +import TodoForm from './components/TodoForm'; +import TodoLists from './components/TodoLists'; + +export default function App() { + const [newOrUpdateTodo, setNewOrUpdateTodo] = useState(''); + const [todos, setTodos] = useState([]); + + const handleSubmit = (e) => { + e.preventDefault(); + setTodos([...todos, { id: nanoid(6), text: newOrUpdateTodo }]); + setNewOrUpdateTodo(''); + }; + + const handleChange = (e) => { + setNewOrUpdateTodo(e.target.value); + }; + + const handleClickComplete = (clickedItemID) => { + setTodos(todos.filter((item) => item.id !== clickedItemID)); + }; + + return ( +
+
+ + +
+ ); +} 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/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..9847045c --- /dev/null +++ b/src/components/TodoForm.jsx @@ -0,0 +1,10 @@ +import React from 'react'; + +export default function TodoForm({ newOrUpdateTodo, onSubmit, onChange }) { + return ( +
+ + +
+ ); +} diff --git a/src/components/TodoLists.jsx b/src/components/TodoLists.jsx new file mode 100644 index 00000000..8de65e7a --- /dev/null +++ b/src/components/TodoLists.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import NoneTodos from './NoneTodos'; +import ShowTodos from './ShowTodos'; + +const isEmpty = (arr = []) => arr.length === 0; + +export default function TodoLists({ todos, onClickComplete }) { + if (isEmpty(todos)) { + return ; + } + return ; +} 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'));