-
Notifications
You must be signed in to change notification settings - Fork 13
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
[3주차] 김주현 미션 제출합니다. #24
base: master
Are you sure you want to change the base?
Changes from all commits
1060f71
e9b0b59
f706326
1bd49df
6e72df2
64bd39a
c7e5ba6
6c730b1
ac2cfa0
9e3249b
84e1de5
702e7b2
9f8a828
40d561f
baad1a9
3157e03
0ed9384
c9b9b1d
28ba17e
f8e3a76
5af6ec6
0841c66
e0a8fe5
10c507c
b073050
543047b
903d7aa
1c47cee
bf2378f
80382ef
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 |
---|---|---|
|
@@ -6,9 +6,15 @@ | |
"@testing-library/jest-dom": "^5.16.2", | ||
"@testing-library/react": "^12.1.4", | ||
"@testing-library/user-event": "^13.5.0", | ||
"@types/node": "^17.0.23", | ||
"@types/react": "^17.0.43", | ||
"@types/react-dom": "^17.0.14", | ||
"@types/styled-components": "^5.1.24", | ||
"react": "^17.0.2", | ||
"react-dom": "^17.0.2", | ||
"react-scripts": "5.0.0", | ||
"styled-components": "^5.3.3", | ||
"typescript": "^4.6.3", | ||
"web-vitals": "^2.1.4" | ||
}, | ||
"scripts": { | ||
|
@@ -34,5 +40,9 @@ | |
"last 1 firefox version", | ||
"last 1 safari version" | ||
] | ||
} | ||
}, | ||
"main": "index.js", | ||
"repository": "https://github.com/corinthionia/react-todo-15th.git", | ||
"author": "Joohyun Kim <[email protected]>", | ||
"license": "MIT" | ||
} |
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { COLORS } from './constants/COLORS'; | ||
import { GlobalStyle } from './styles/GlobalStyle'; | ||
import styled from 'styled-components'; | ||
import InputField from './components/InputField'; | ||
import ItemList from './components/ItemList'; | ||
|
||
function App() { | ||
return ( | ||
<> | ||
<GlobalStyle /> | ||
<Wrapper> | ||
<Header>투두리스트</Header> | ||
<InputField /> | ||
<ItemList isDoneList={false} /> | ||
<ItemList isDoneList={true} /> | ||
</Wrapper> | ||
</> | ||
); | ||
} | ||
|
||
const Wrapper = styled.main` | ||
width: 350px; | ||
height: 700px; | ||
|
||
border-radius: 16px; | ||
border: 1px solid ${COLORS.border}; | ||
overflow: hidden; | ||
|
||
background: ${COLORS.background}; | ||
`; | ||
|
||
const Header = styled.header` | ||
height: 10%; | ||
font-size: 28px; | ||
padding-left: 16px; | ||
|
||
display: flex; | ||
align-items: center; | ||
|
||
border-bottom: 1px solid ${COLORS.border}; | ||
`; | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,71 @@ | ||||||
import { useContext } from 'react'; | ||||||
import styled from 'styled-components'; | ||||||
import useInput from '../hooks/useInput'; | ||||||
import { COLORS } from '../constants/COLORS'; | ||||||
import { ItemListContext } from '../contexts/ItemListContext'; | ||||||
import { IItem } from '../types/types'; | ||||||
|
||||||
const InputField = () => { | ||||||
const { inputText, handleInputChange, resetInputText } = useInput(''); | ||||||
const { setItemListHandler } = useContext(ItemListContext); | ||||||
|
||||||
const handleFormSubmit = (e: React.SyntheticEvent) => { | ||||||
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.
Suggested change
제가 지난주에 현재님 리뷰에 결론부터 말씀드리면 지양하는게 좋습니다. 리액트에서 이벤트 처리를 할 때, 이벤트 핸들러에 전달받는 이벤트 객체는 자바스크립트의 네이티브 이벤트 객체가 아닙니다, 왜 그럴까요? SPA의 특성상 사용자와의 인터랙션이 빈번하게 발생합니다. 많은 이벤트가 발생한다는 의미죠. 하지만 그럴 때 마다 이벤트 객체를 매번 생성하게 된다면 메모리 공간과 오버헤드가 영향을 미칠 수 있겠죠. 그래서 리액트는 이벤트가 발생할 때 마다 그래서 리액트에서는 어떤 이벤트가 발생하던 간에, 따라서, 만약 다중 이벤트를 폼에서 처리해야 한다면, 해당하는 이벤트들 끼리 묶어 하나의 커스텀 타입을 만드는 것이 더욱 좋은 방법일 것 같습니다. |
||||||
e.preventDefault(); | ||||||
|
||||||
if (inputText.replace(/\s+/g, '')) { | ||||||
const todo = { id: Date.now(), text: inputText, isDone: false }; | ||||||
setItemListHandler((itemList: IItem[]) => [todo, ...itemList]); | ||||||
} else { | ||||||
alert('할일을 입력해 주세요'); | ||||||
} | ||||||
|
||||||
resetInputText(); | ||||||
}; | ||||||
|
||||||
return ( | ||||||
<InputForm onSubmit={handleFormSubmit}> | ||||||
<Input | ||||||
value={inputText} | ||||||
onChange={handleInputChange} | ||||||
spellCheck="false" | ||||||
autoFocus | ||||||
placeholder="할일을 입력하세요" | ||||||
/> | ||||||
<SubmitBtn onClick={handleFormSubmit}>➕</SubmitBtn> | ||||||
</InputForm> | ||||||
); | ||||||
}; | ||||||
|
||||||
const InputForm = styled.form` | ||||||
height: 10%; | ||||||
display: flex; | ||||||
align-items: center; | ||||||
justify-content: space-evenly; | ||||||
`; | ||||||
|
||||||
const Input = styled.input` | ||||||
width: 77.5%; | ||||||
height: 50%; | ||||||
padding-left: 2.5%; | ||||||
|
||||||
border-radius: 8px; | ||||||
border: 1px solid ${COLORS.border}; | ||||||
background: ${COLORS.background}; | ||||||
|
||||||
::placeholder { | ||||||
color: ${COLORS.placeholder}; | ||||||
} | ||||||
`; | ||||||
|
||||||
const SubmitBtn = styled.button` | ||||||
width: 10%; | ||||||
height: 50%; | ||||||
|
||||||
background: none; | ||||||
border-radius: 8px; | ||||||
border: 1px solid ${COLORS.border}; | ||||||
|
||||||
cursor: pointer; | ||||||
`; | ||||||
|
||||||
export default InputField; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import styled, { css } from 'styled-components'; | ||
import { COLORS } from '../constants/COLORS'; | ||
import { IIsDoneList } from '../types/types'; | ||
|
||
const Item = ({ | ||
id, | ||
text, | ||
isDoneList, | ||
handleTextClick, | ||
handleDeleteBtnClick, | ||
}) => { | ||
return ( | ||
<ItemWrapper key={id}> | ||
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. 아 여기서 key값을 줘도 되는군요 처음 알았네요 |
||
<ItemText id={id} isDoneList={isDoneList} onClick={handleTextClick}> | ||
{text} | ||
</ItemText> | ||
<DeleteBtn | ||
src={`${process.env.PUBLIC_URL}/img/bin.png`} | ||
id={id} | ||
onClick={handleDeleteBtnClick} | ||
/> | ||
</ItemWrapper> | ||
); | ||
}; | ||
|
||
const ItemWrapper = styled.div` | ||
width: 90%; | ||
margin: 12px; | ||
|
||
display: flex; | ||
align-items: center; | ||
`; | ||
|
||
const ItemText = styled.span<IIsDoneList>` | ||
${({ isDoneList }) => | ||
isDoneList && | ||
css` | ||
color: ${COLORS.lightgrey}; | ||
text-decoration: line-through; | ||
`} | ||
|
||
cursor: pointer; | ||
`; | ||
|
||
const DeleteBtn = styled.img` | ||
width: 16px; | ||
height: 16px; | ||
margin-left: 8px; | ||
|
||
cursor: pointer; | ||
`; | ||
|
||
export default Item; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { useContext } from 'react'; | ||
import styled from 'styled-components'; | ||
import Item from './Item'; | ||
import { COLORS } from '../constants/COLORS'; | ||
import { ItemListContext } from '../contexts/ItemListContext'; | ||
import { IItem } from '../types/types'; | ||
|
||
const ItemList = ({ isDoneList }) => { | ||
const { itemList, setItemListHandler } = useContext(ItemListContext); | ||
|
||
const filteredList = itemList.filter( | ||
(item: IItem) => item.isDone === isDoneList | ||
); | ||
|
||
const handleTextClick = (e: React.MouseEvent<HTMLButtonElement>) => { | ||
const textElement: HTMLButtonElement = e.currentTarget; | ||
|
||
const newList = (filteredList: IItem[]) => | ||
filteredList.map((item: IItem) => | ||
item.id === parseInt(textElement.id) | ||
? { ...item, isDone: !item.isDone } | ||
: item | ||
); | ||
|
||
setItemListHandler(newList); | ||
}; | ||
Comment on lines
+15
to
+26
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 handleDeleteBtnClick = (e: React.MouseEvent<HTMLButtonElement>) => { | ||
const deleteBtn: HTMLButtonElement = e.currentTarget; | ||
|
||
const newList = (filteredList: IItem[]) => | ||
filteredList.filter((todo: IItem) => todo.id !== parseInt(deleteBtn.id)); | ||
|
||
setItemListHandler(newList); | ||
}; | ||
|
||
return ( | ||
<> | ||
<ListTitle> | ||
{isDoneList | ||
? `${filteredList.length}개의 할일을 완료했어요` | ||
: `${filteredList.length}개의 할일이 남아있어요`} | ||
</ListTitle> | ||
<List> | ||
{filteredList.map(({ id, text }) => ( | ||
<Item | ||
key={id} | ||
id={id} | ||
text={text} | ||
isDoneList={isDoneList} | ||
handleTextClick={handleTextClick} | ||
handleDeleteBtnClick={handleDeleteBtnClick} | ||
/> | ||
))} | ||
</List> | ||
</> | ||
); | ||
}; | ||
|
||
const ListTitle = styled.div` | ||
height: 7.5%; | ||
padding-left: 16px; | ||
|
||
display: flex; | ||
align-items: center; | ||
|
||
font-size: 20px; | ||
|
||
border-top: 1px solid ${COLORS.border}; | ||
border-bottom: 1px solid ${COLORS.border}; | ||
`; | ||
|
||
const List = styled.section` | ||
height: 32.5%; | ||
|
||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
|
||
overflow: auto; | ||
|
||
&::-webkit-scrollbar { | ||
width: 0.75rem; | ||
} | ||
|
||
&::-webkit-scrollbar-thumb { | ||
background: ${COLORS.scrollbar}; | ||
background-clip: padding-box; | ||
border-radius: 10px; | ||
border: 0.25rem solid transparent; | ||
} | ||
`; | ||
|
||
export default ItemList; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const COLORS = { | ||
border: '#ececec', | ||
darkgrey: '#3d3d3d', | ||
lightgrey: 'rgba(255, 255, 255, 0.3)', | ||
placeholder: 'rgba(255, 255, 255, 0.7)', | ||
scrollbar: 'rgba(255, 255, 255, 0.4)', | ||
background: 'rgba(255, 255, 255, 0.1)', | ||
}; |
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.
같은 컴포넌트로 할 일 목록, 완료 목록을 구분해서 생산성이 더 높은 것 같아요
저는 구분해서 두 개의 컴포넌트를 만들었는데 이런 식으로 만드니 더 좋네요.
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.
항상 어려운 건 설계 단계인 것 같네요
저도 많이 배워갑니다~
아래는 읽어보면 좋을 것 같은 링크입니다~
리액트 설계 가이드
컴포넌트 분리