-
Notifications
You must be signed in to change notification settings - Fork 26
[이연수] Sprint5 #132
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
[이연수] Sprint5 #132
The head ref may contain hidden characters: "React-\uC774\uC5F0\uC218-sprint5"
Conversation
…, 컴포넌트 버튼/카드/드롭다운/페이지네이션 수정)
|
스프리트 미션 하시느라 수고 많으셨어요. |
검토해주셔서 감사합니다.
|
| <meta property="og:type" content="website" /> | ||
| <meta property="og:site_name" content="판다마켓" /> | ||
| <meta property="og:title" content="판다마켓" /> | ||
| <meta property="og:description" content="일상의 모든 물건을 거래해보세요" /> | ||
| <meta property="og:image" content="/og-img.png" /> | ||
| <!-- Favicon --> | ||
| <link rel="icon" href="/favicon.ico" /> | ||
| <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" /> | ||
| <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" /> | ||
| <link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png" /> |
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.
크으 ~ 메타 태그까지 빠트리지 않고 꼼꼼하시군요 ! 👍
| const getItems = async ({ page = 1, pageSize = 10, orderBy = "recent", keyword = "" }) => { | ||
| try { | ||
| const { data } = await instance.get("/products", { | ||
| params: { | ||
| page, | ||
| pageSize, | ||
| orderBy, | ||
| keyword, | ||
| }, | ||
| }); | ||
| return data; | ||
| } catch (error) { | ||
| console.error(error); | ||
| throw error; | ||
| } | ||
| }; |
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.
훌륭합니다 ! 더 할 말이 없군요 ! 👍
파라메터의 기본값도 적절하고, 받아야 할 파라메터도 필요한 것들만 잘 받고 있네요.
axios의 params 활용도 좋고 try ... catch를 통한 예외처리도 ! ㅎㅎㅎ
너무 칭찬만 했는데.. 칭찬 받을 명분이 충분하셨습니다 👍
| const CardItemWrapper = styled.li` | ||
| position: relative; | ||
| width: 100%; | ||
| `; | ||
|
|
||
| const CardThumbnail = styled.div` | ||
| width: 100%; | ||
| aspect-ratio: 1 / 1; | ||
| border-radius: 16px; | ||
| overflow: hidden; | ||
| transition: all 0.2s; | ||
| & img { | ||
| width: 100%; | ||
| height: 100%; | ||
| object-fit: cover; | ||
| } | ||
| ${CardItemWrapper}:hover & { | ||
| opacity: 0.8; | ||
| } | ||
| ${Midia("sm")} { | ||
| border-radius: 12px; | ||
| } | ||
| `; | ||
|
|
||
| const CardInfo = styled.div` | ||
| margin-top: 16px; | ||
| display: grid; | ||
| gap: 6px 0; | ||
| word-break: break-all; | ||
| `; | ||
|
|
||
| const CardTitle = styled.div` | ||
| ${typography["text-md-medium"]}; | ||
| white-space: nowrap; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| `; | ||
|
|
||
| const CardPrice = styled.div` | ||
| ${typography["text-lg-bold"]}; | ||
| `; | ||
|
|
||
| const CardLikes = styled.div` | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 0 4px; | ||
| & .count { | ||
| ${typography["text-xs-medium"]}; | ||
| } | ||
| `; |
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.
크으 .. 이런게 styled-components의 매력인 것 같아요.
스타일과 컴포넌트의 영역이 명확히 구분되어서 가독성도 좋고, 자바스크립트를 유연하게 사용할 수 있는 점이 참 매력적으로 느껴지네요.
잘 활용하고 계십니다 👍
| const CardItem = ({ images, name, price, favoriteCount }) => { | ||
| const thumbnailUrl = images && images.length > 0 ? images[0] : defaultImg; |
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.
해당 컴포넌트는 images가 필요한 것이 아닌 thumbnail이 필요한 것 같군요?
그렇다면 다음과 같이 바꿔볼 수 있을거예요:
| const CardItem = ({ images, name, price, favoriteCount }) => { | |
| const thumbnailUrl = images && images.length > 0 ? images[0] : defaultImg; | |
| const CardItem = ({ thumbnail = defaultImg, name, price, favoriteCount }) => { |
그리고 해당 컴포넌트를 호출하는 곳에서 결정해줄 수 있어요 😉:
<CardItem thumbnail={(images && images.length > 0) && images[0]}이렇게 하면 카드 컴포넌트가 진짜 필요한 매개인자로 구성할 수 있겠군요 !
| const useClickOutside = (ref, onClickOutside) => { | ||
| useEffect(() => { | ||
| const handleClickOutside = e => { | ||
| if (!ref.current || ref.current.contains(e.target)) { | ||
| return; | ||
| } | ||
| onClickOutside(e); | ||
| }; | ||
|
|
||
| document.addEventListener("mousedown", handleClickOutside); | ||
| document.addEventListener("touchstart", handleClickOutside); | ||
| return () => { | ||
| document.removeEventListener("mousedown", handleClickOutside); | ||
| document.removeEventListener("touchstart", handleClickOutside); | ||
| }; | ||
| }, [ref, onClickOutside]); | ||
| }; |
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.
크으~~~~ 재사용성을 고려한 커스텀 훅 !
이렇게 인터랙션을 훅으로 담아두니까 모달, 드롭다운, 팝오버 등 여러 곳에서 사용되기 너무 좋을 것 같아요.
재사용성을 위해서 커스텀 훅을 설계하시다니.
미션 5를 진행하시는게 아까울 정도네요. 😂😂😂
| const useResponsiveView = () => { | ||
| const [view, setView] = useState(() => { | ||
| const width = window.innerWidth; | ||
| if (width <= BREAKPOINTS.mobileMax) return "mobile"; | ||
| if (width <= BREAKPOINTS.tabletMax) return "tablet"; | ||
| return "desktop"; | ||
| }); | ||
|
|
||
| useEffect(() => { | ||
| const handleResize = () => { | ||
| const width = window.innerWidth; | ||
| if (width <= BREAKPOINTS.mobileMax) { | ||
| setView("mobile"); | ||
| } else if (width <= BREAKPOINTS.tabletMax) { | ||
| setView("tablet"); | ||
| } else { | ||
| setView("desktop"); | ||
| } | ||
| }; | ||
|
|
||
| window.addEventListener("resize", handleResize); | ||
| return () => window.removeEventListener("resize", handleResize); | ||
| }, []); | ||
|
|
||
| return view; | ||
| }; |
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.
useResponsiveView도 우리 판다마켓에서 재사용성이 좋아보이네요 👍👍👍
| const toggle = useCallback(() => setState(prev => !prev), []); | ||
| const setOn = useCallback(() => setState(true), []); | ||
| const setOff = useCallback(() => setState(false), []); |
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.
setter(setState)를 그대로 반환하지 않고 핸들러로 감싸서 반환했군요 ! 👍
훌륭합니다. 이렇게 콜백으로 감싸서 전달하면 추 후 유지관리에 용이할거예요. 👍👍👍
| const ITEMS_DISPLAY_COUNT = { | ||
| desktop: 10, | ||
| tablet: 6, | ||
| mobile: 4, | ||
| }; |
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.
굿굿. 컴포넌트와 관련없는 상수 및 변수를 컴포넌트 바깥에 선언하셨군요 !
훌륭합니다. 전반적으로 연수님은 리액트 활용에 대해서 잘 적응하신 것으로 보이는군요 ! 👍
|
간만에 연수님 코드를 보네요 ㅎㅎㅎ 반가운 마음으로 리뷰하였습니다. 🤣 |
URL
배포 주소 | 판다마켓
요구사항
기본
중고마켓
중고마켓 반응형
Desktop : 4개 보이기
Tablet : 2개 보이기
Mobile : 1개 보이기
Desktop : 12개 보이기
Tablet : 6개 보이기
Mobile : 4개 보이기
심화
주요 변경사항
스크린샷
멘토에게