-
Notifications
You must be signed in to change notification settings - Fork 26
[정상인] sprint7 #117
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
[정상인] sprint7 #117
The head ref may contain hidden characters: "React-\uC815\uC0C1\uC778-sprint7"
Conversation
|
스프리트 미션 하시느라 수고 많으셨어요. |
|
역시 상인님... 질문을 '읽는 사람'을 고려하여 정말 잘 전달주셨네요. 커뮤니케이션 스킬이 남다르십니다. 🥺 URL parameter 를 가져오고 각 컴포넌트의 props로 넘겨주는 것과, 각 컴포넌트들에서 URL parameter를 가져오는 것에 대해서 고민을 많이했습니다. 의견이 궁금합니다.제 생각에서는 컴포넌트가 다른 출처에서 받은 |
(질문) 태그 인풋은 엔터를 누를 때 등록되게 해서 따로 form을 구성했는데 이 방법이 어색한 느낌이 들어서 어떤지 궁금합니다.헐... 죄송합니다 제가 코드를 잘못 읽고 질문에 대한 답변을 잘 못했네요 ㅠㅠㅠ HTML 표준 위반이 아니며, 편하고 유용한 방법일 것으로 사료되기는 하나 일반적으로 하나의 제출 단위로 필요한 다음과 같이 해볼 수도 있겠네요. <form className="addItem-form" onSubmit={handleSubmitAddItem}>
<fieldset>
<legend className="sr-only">상품 기본 정보</legend>
<AddItemFormHeader formData={formData} />
<AddItemImage ... />
<AddItemName value={formData.name} onChange={(e)=>handleChange(e,"name")} />
<AddItemDescription ... />
<AddItemPrice ... />
</fieldset>
<fieldset>
<AddItemTag
value={inputValueTag}
onChange={(e) => setInputValueTag(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
handleSubmitTag();
}
}}
/>
<button type="button" onClick={handleSubmitTag}>태그 추가</button>
</fieldset>
<div className="actions">
<button type="submit">상품 등록</button>
</div>
</form>다만, '일반적' 이라는 말이 참 모호하죠. 하핳.. 지금 상인님께서 작성하신 코드는 그렇다고 해서 HTML의 표준을 위배하고 있지는 않기에 그대로 두셔도 무방할 것으로 보입니다. |
| DropDown.header = ({ children }) => { | ||
| return <div>{children}</div>; |
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.
크으.. 상인님 코드를 볼 때마다 느끼는거지만 유지보수에 신경을 많이 쓰시는 것 같아요.
그냥 Dorpdown children에 <div>로처리하셨을 수도 있었을텐데, 드롭다운이 공통적으로 변경되는 것도 고려하여 이렇게 설계하신게 느껴집니다.. 👍
| const ScrollToTop = () => { | ||
| const { pathname } = useLocation(); | ||
|
|
||
| useEffect(() => { | ||
| window.scrollTo(0, 0); | ||
| }, [pathname]); | ||
|
|
||
| return null; | ||
| }; |
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 ScrollToTop = () => { | |
| const { pathname } = useLocation(); | |
| useEffect(() => { | |
| window.scrollTo(0, 0); | |
| }, [pathname]); | |
| return null; | |
| }; | |
| const useFixTop = () => { | |
| const { pathname } = useLocation(); | |
| useEffect(() => { | |
| window.scrollTo(0, 0); | |
| }, [pathname]); | |
| }; |
컴포넌트는 보통 실체하는 ui가 있을 때 사용되는건데 이건 컴포넌트로 보긴 어렵겠네요.
| <TagBadge name={tag} onDelete={() => onDelete(index)} /> | ||
| <TagBadge | ||
| name={tag} | ||
| onDelete={onDelete ? () => onDelete(index) : undefined} |
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.
해당 코드는 다음과 같이 함축할 수 있겠네요 !
| onDelete={onDelete ? () => onDelete(index) : undefined} | |
| onDelete={onDelete && () => onDelete(index)} |
어떻게 이게 가능할까요?
- 자바스크립트에서
&&연산자는 앞의 값이 truthy일 때만 뒤의 값을 평가합니다. - 즉,
onDelete가 존재하지 않으면false가 반환되어onDeleteprops에는false가 들어갑니다. - React는
false,null,undefined를 렌더링하지 않는 값으로 처리하므로, 결과적으로onDelete가 전달되지 않은 것과 동일하게 동작합니다.
| export const QUESTION_PLACEHOLDER = | ||
| "개인정보를 공유 및 요청하거나, 명예 훼손, 무단 광고, 불법 정보 유포시 모니터링 후 삭제될 수 있으며, 이에 대한 민형사상 책임은 게시자에게 있습니다."; |
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.
해당 상수는 파일을 분리함으로써 어떤 이득을 취할 수 있을까요? 🤔
사용되는 컴포넌트와 멀리 떨어져 있기에 개발 경험이 떨어질 수 있으며 유지보수를 위하기에는 재사용될 가능성이 낮아보일 것으로 사료되는군요 ..!
다국어를 지원하는 경우 텍스트들을 중앙집중형으로 관리하기도 하나, 지금은 다른 케이스인 것으로 보이는군요 😉
| export const useGetProductCommentsQuery = ({ productId, limit, cursor }) => { | ||
| return useQuery({ | ||
| queryKey: ["getProductComments", productId, limit], | ||
| queryFn: () => getProductComments({ productId, limit, cursor }), | ||
| staleTime: 300000, | ||
| select: (response) => response.data, | ||
| }); |
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.
크으.. 기초 프로젝트 때 익히신 react-query를 사용해보셨군요 ! 😊
배우신 것을 바로 응용해보시는 상인님의 학습 열정 리스펙합니다 👍👍
|
|
||
| export const useGetProductCommentsQuery = ({ productId, limit, cursor }) => { | ||
| return useQuery({ | ||
| queryKey: ["getProductComments", productId, limit], |
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.
그런데, cursor도 함께 쿼리키에 포함하여야 하지 않을까요?
| queryKey: ["getProductComments", productId, limit], | |
| queryKey: ["getProductComments", productId, limit, cursor], |
두 번째 커서, 세 번째 커서, 각기 다른 결과값을 가져올 것으로 보여서요 !
| const { data, isLoading, isError, error } = useGetProductCommentsQuery({ | ||
| productId, | ||
| limit: 3, | ||
| }); | ||
|
|
||
| if (isLoading) { | ||
| return <LoadingSpinner />; | ||
| } | ||
|
|
||
| if (isError) { | ||
| return <ErrorMessage errorMessage={error.message} />; | ||
| } | ||
|
|
||
| const comments = data.list; |
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 { data, isLoading, isError, error } = useGetProductCommentsQuery({ | |
| productId, | |
| limit: 3, | |
| }); | |
| if (isLoading) { | |
| return <LoadingSpinner />; | |
| } | |
| if (isError) { | |
| return <ErrorMessage errorMessage={error.message} />; | |
| } | |
| const comments = data.list; | |
| const { data: { | |
| list: comments | |
| }, isLoading, isError, error } = useGetProductCommentsQuery({ | |
| productId, | |
| limit: 3, | |
| }); | |
| if (isLoading) { | |
| return <LoadingSpinner />; | |
| } | |
| if (isError) { | |
| return <ErrorMessage errorMessage={error.message} />; | |
| } |
const comments = data.list;를 따로 선언하지 않고 받아올 때 구조분해 할당으로 선언과 동시에 별칭도 붙일 수 있어요 😉
| const { | ||
| data: productInfo, | ||
| isLoading, | ||
| isError, | ||
| error, | ||
| } = useGetProductDetailQuery({ | ||
| productId, | ||
| }); | ||
|
|
||
| if (isLoading) return <LoadingSpinner />; | ||
|
|
||
| if (isError) return <ErrorMessage errorMessage={error.message} />; |
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.
아으.. 너무 깔끔합니다 !
loading, error 처리. 리액트 쿼리의 기본적인 상태들을 사용하시니까 코드가 훨씬 간결해졌네요 👍
|
크으 ~ 너무 좋습니다 상인님. |
요구사항
배포링크
기본
심화
주요 변경사항
스크린샷
멘토에게
변경 전
변경 후 : 각 컴포넌트에서 직접 가져옵니다.