-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[2주차] 고주희 미션 제출합니다. #1
base: main
Are you sure you want to change the base?
Changes from all commits
b1ac074
5773413
a4bb4f0
c83acb3
fb3aea3
4ef040f
4aba320
b2a22d4
4229727
faaa434
7cd2a1e
5fc163a
8e2b311
c654aaa
edde4be
1c8a149
687e1bb
81f27ff
bc91bb8
c7cc678
25f4859
d073816
52584a1
a3cc61b
0208232
07a59b1
9f9d6fc
b02d33f
32e1632
7ab005d
d20fb37
49a62c1
d00702e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,5 +2,5 @@ | |
"trailingComma": "es5", | ||
"tabWidth": 2, | ||
"semi": true, | ||
"singleQuote": true | ||
"singleQuote": false | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,8 +10,12 @@ | |
"preview": "vite preview" | ||
}, | ||
"dependencies": { | ||
"@emotion/react": "^11.13.3", | ||
"@emotion/styled": "^11.13.0", | ||
"prop-types": "^15.8.1", | ||
"react": "^18.3.1", | ||
"react-dom": "^18.3.1" | ||
"react-dom": "^18.3.1", | ||
"uuid": "^10.0.0" | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저희는 yarn을 사용하기 때문에 |
||
"devDependencies": { | ||
"@eslint/js": "^9.9.0", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,214 @@ | ||
function App() { | ||
return <div>React Todo</div>; | ||
} | ||
import TodoSection from "./components/shared/TodoSection"; | ||
import useLocalStorage from "./components/hooks/useLocalStorage"; | ||
import { Style } from "./components/styles/App.styles"; | ||
import Header from "./Header"; | ||
import { Global, css } from "@emotion/react"; | ||
|
||
export default function App() { | ||
const [todos, setTodos] = useLocalStorage("todos", []); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호 로컬스토리지 관련 로직을 커스텀훅으로 만드셨군요 👍🏻👍🏻 |
||
const [completed, setCompleted] = useLocalStorage("completed", []); | ||
|
||
const handleFormSubmit = (todo) => { | ||
setTodos((prev) => [todo, ...prev]); | ||
}; | ||
|
||
const handleToggleTodo = (index, isCompleted) => { | ||
if (isCompleted) { | ||
const newCompleted = completed.filter((_, i) => i !== index); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이벤트 핸들러 네이밍 잘 지켜주셨군요 ! 저도 잘 지켜야 할 텐데 말입니다.. |
||
const todoToMove = completed[index]; | ||
setCompleted(newCompleted); | ||
setTodos((prev) => [todoToMove, ...prev]); | ||
} else { | ||
const newTodos = todos.filter((_, i) => i !== index); | ||
const completedTodo = todos[index]; | ||
setTodos(newTodos); | ||
setCompleted((prev) => [completedTodo, ...prev]); | ||
} | ||
}; | ||
|
||
const deleteTodo = (index) => { | ||
const newTodos = todos.filter((_, i) => i !== index); | ||
setTodos(newTodos); | ||
}; | ||
|
||
const deleteDone = (index) => { | ||
const newCompleted = completed.filter((_, i) => i !== index); | ||
setCompleted(newCompleted); | ||
}; | ||
|
||
export default App; | ||
return ( | ||
<div> | ||
<Global | ||
styles={css` | ||
@font-face { | ||
font-family: "DungGeunMo"; | ||
src: url("https://fastly.jsdelivr.net/gh/projectnoonnu/[email protected]/DungGeunMo.woff") | ||
format("woff"); | ||
font-weight: normal; | ||
font-style: normal; | ||
} | ||
|
||
html, | ||
body, | ||
div, | ||
span, | ||
applet, | ||
object, | ||
iframe, | ||
h1, | ||
h2, | ||
h3, | ||
h4, | ||
h5, | ||
h6, | ||
p, | ||
blockquote, | ||
pre, | ||
a, | ||
abbr, | ||
acronym, | ||
address, | ||
big, | ||
cite, | ||
code, | ||
del, | ||
dfn, | ||
em, | ||
img, | ||
ins, | ||
kbd, | ||
q, | ||
s, | ||
samp, | ||
small, | ||
strike, | ||
strong, | ||
sub, | ||
sup, | ||
tt, | ||
var, | ||
b, | ||
u, | ||
i, | ||
center, | ||
dl, | ||
dt, | ||
dd, | ||
ol, | ||
ul, | ||
li, | ||
fieldset, | ||
form, | ||
label, | ||
legend, | ||
table, | ||
caption, | ||
tbody, | ||
tfoot, | ||
thead, | ||
tr, | ||
th, | ||
td, | ||
article, | ||
aside, | ||
canvas, | ||
details, | ||
embed, | ||
figure, | ||
figcaption, | ||
footer, | ||
header, | ||
hgroup, | ||
menu, | ||
nav, | ||
output, | ||
ruby, | ||
section, | ||
summary, | ||
time, | ||
mark, | ||
audio, | ||
video { | ||
margin: 0; | ||
padding: 0; | ||
border: 0; | ||
font-size: 100%; | ||
font: inherit; | ||
vertical-align: baseline; | ||
} | ||
|
||
article, | ||
aside, | ||
details, | ||
figcaption, | ||
figure, | ||
footer, | ||
header, | ||
hgroup, | ||
menu, | ||
nav, | ||
section { | ||
display: block; | ||
} | ||
body { | ||
font-family: "DungGeunMo"; | ||
width: 100%; | ||
height: 100vh; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
background-color: #595ca1; | ||
background-image: url("/public/arcade.png"); | ||
background-size: 1430px; | ||
background-position: 49%; | ||
background-repeat: no-repeat; | ||
} | ||
input { | ||
font-family: "DungGeunMo"; | ||
} | ||
ol, | ||
ul { | ||
list-style: none; | ||
} | ||
blockquote, | ||
q { | ||
quotes: none; | ||
} | ||
blockquote:before, | ||
blockquote:after, | ||
q:before, | ||
q:after { | ||
content: ""; | ||
content: none; | ||
} | ||
table { | ||
border-collapse: collapse; | ||
border-spacing: 0; | ||
} | ||
`} | ||
/> | ||
|
||
<Style.AppWrapper> | ||
<Header handleFormSubmit={handleFormSubmit} /> | ||
|
||
<Style.TodoSectionWrapper> | ||
<TodoSection | ||
title="IN PROGRESS" | ||
items={todos} | ||
onToggle={handleToggleTodo} | ||
onDelete={deleteTodo} | ||
isCompleted={false} | ||
/> | ||
|
||
<TodoSection | ||
title="COMPLETED" | ||
items={completed} | ||
onToggle={handleToggleTodo} | ||
onDelete={deleteDone} | ||
isCompleted={true} | ||
/> | ||
</Style.TodoSectionWrapper> | ||
</Style.AppWrapper> | ||
</div> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import Input from "./components/shared/Input"; | ||
import { Text } from "./components/shared/UI"; | ||
import { Style } from "./components/styles/App.styles"; | ||
|
||
export default function Header({ handleFormSubmit }) { | ||
return ( | ||
<Style.Header> | ||
<Style.TitleWrapper> | ||
<Style.GameIcon src="game2.png" alt="game" /> | ||
<Text.Title>SCHEDULE</Text.Title> | ||
</Style.TitleWrapper> | ||
<Input addTodo={handleFormSubmit} /> | ||
</Style.Header> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { useState, useCallback } from "react"; | ||
|
||
export default function useInput() { | ||
const [value, setValue] = useState(""); | ||
|
||
const handleChange = useCallback((event) => { | ||
setValue(event.target.value); | ||
}, []); | ||
|
||
return [value, handleChange, setValue]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { useState, useEffect } from "react"; | ||
|
||
function useLocalStorage(key, initialValue) { | ||
const [storedValue, setStoredValue] = useState(() => { | ||
const item = localStorage.getItem(key); | ||
return item ? JSON.parse(item) : initialValue; | ||
}); | ||
|
||
useEffect(() => { | ||
localStorage.setItem(key, JSON.stringify(storedValue)); | ||
}, [key, storedValue]); | ||
|
||
return [storedValue, setStoredValue]; | ||
} | ||
Comment on lines
+3
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 이런식으로 localstorage를 따로 뺴서 적용해봐야겠네요! 덕부에 배워갑니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 아래 코드처럼 로컬스토리지를 구현했습니다. 근데 주희님은 커스텀훅으로 따로 만드셨더라구요! 따로 만들게 된 이유와 따로 만들게 되면 이점이 무엇인지 궁금합니다.
|
||
|
||
export default useLocalStorage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import useInput from "../hooks/useInput"; | ||
import { Style } from "../styles/Input.styles"; | ||
|
||
export default function Input({ addTodo }) { | ||
const [inputValue, handleChange, setInputValue] = useInput(""); | ||
Comment on lines
+1
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. useInput 훅을 활용해 입력 값의 상태를 관리하는 부분이 매우 깔끔한 것 같습니다!! |
||
|
||
const handleFormSubmit = (event) => { | ||
event.preventDefault(); | ||
if (inputValue.trim() !== "") { | ||
addTodo(inputValue); | ||
setInputValue(""); | ||
} else { | ||
alert("입력된 할 일이 없어요, 다시 확인해주세요!"); | ||
} | ||
}; | ||
|
||
return ( | ||
<form onSubmit={handleFormSubmit}> | ||
<Style.InputWrapper> | ||
<Style.Input | ||
type="text" | ||
value={inputValue} | ||
onChange={handleChange} | ||
placeholder="할 일을 입력해주세요." | ||
/> | ||
<Style.Button type="submit">+</Style.Button> | ||
</Style.InputWrapper> | ||
</form> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import TodoItem from "./TodoItem"; | ||
import { Style } from "../styles/Todo.styles"; | ||
|
||
export default function Todo({ items, onToggle, onDelete, isCompleted }) { | ||
return ( | ||
<Style.Wrapper> | ||
{items.map((todo, index) => ( | ||
<TodoItem | ||
key={index} | ||
todo={todo} | ||
isCompleted={isCompleted} | ||
onToggle={() => onToggle(index, isCompleted)} | ||
onDelete={() => onDelete(index)} | ||
/> | ||
))} | ||
</Style.Wrapper> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
프리티어 설정 확인해 주세요!