Conversation
| value={inputValue} | ||
| onChange={(e) => setInputValue(e.target.value)} | ||
| onFocus={handleFocus} | ||
| onKeyDown={handlePressEnter} |
There was a problem hiding this comment.
사진처럼 onKeyDown 썼을 때 한글 마지막이 두번 입력 되는데 한번 읽어보세요
| <RightWrapper> | ||
| <SmileICon /> | ||
| {!isFocus ? ( | ||
| <HashtagICon /> | ||
| ) : inputValue?.length !== 0 ? ( | ||
| <ActiveIcon onClick={handleText} /> | ||
| ) : ( | ||
| <InActiveIcon /> | ||
| )} | ||
| </RightWrapper> |
There was a problem hiding this comment.
내부 로직을 밖으로 분리하면 가독성이나 유지보수하기 좋을 것 같아요
| <RightWrapper> | |
| <SmileICon /> | |
| {!isFocus ? ( | |
| <HashtagICon /> | |
| ) : inputValue?.length !== 0 ? ( | |
| <ActiveIcon onClick={handleText} /> | |
| ) : ( | |
| <InActiveIcon /> | |
| )} | |
| </RightWrapper> | |
| function renderIcon() { | |
| if (!isFocus) { | |
| return <HashtagIcon />; | |
| } else if (inputValue?.length !== 0) { | |
| return <ActiveIcon onClick={handleText} />; | |
| } else { | |
| return <InActiveIcon />; | |
| } | |
| } | |
| <RightWrapper> | |
| <SmileIcon /> | |
| {renderIcon()} | |
| </RightWrapper> |
| <Link to={`/message/${userId}`}> | ||
| <OneChat key={userId} lastText={lastText} lastTime={lastTime} user={user} userId={userId} /> | ||
| </Link> |
There was a problem hiding this comment.
key를 Link에 적용하는 건 어떨까요
| <Link to={`/message/${userId}`}> | |
| <OneChat key={userId} lastText={lastText} lastTime={lastTime} user={user} userId={userId} /> | |
| </Link> | |
| <Link to={`/message/${userId}`} key={userId}> | |
| <OneChat lastText={lastText} lastTime={lastTime} user={user} userId={userId} /> | |
| </Link> |
jiwonnchoi
left a comment
There was a problem hiding this comment.
안녕하세요 혜인님:) 지난 주차에 이어서 이번 주 과제도 너무 수고하셨습니다🔥
컴포넌트를 잘 쪼개어주셔서 한 파일 당 코드가 길어지지 않아 리뷰하면서도 편했던 것 같아요! 코드 보면서 저도 새롭게 많이 알아갑니다❤️🔥
| "import/no-extraneous-dependencies": 0, // 테스트 또는 개발환경을 구성하는 파일에서는 devDependency 사용을 허용 | ||
| "react/prop-types": 0, | ||
| "react/jsx-filename-extension": [2, { extensions: [".js", ".jsx", ".ts", ".tsx"] }], | ||
| "jsx-a11y/no-noninteractive-element-interactions": 0, | ||
| "eol-last": ["error", "always"], // line의 가장 마지막 줄에는 개행 넣기" | ||
| "simple-import-sort/imports": "error", // import 정렬 | ||
| "no-multi-spaces": "error", // 스페이스 여러개 금지 |
There was a problem hiding this comment.
import를 저는 수동으로 구분해서 정렬해주었는데 eslint 설정을 잘 활용하신 것 같아요!! 저도 다음에 써봐야겠습니당👍
| function App() { | ||
| return ( | ||
| <ThemeProvider theme={theme}> | ||
| <RouterProvider router={router} /> |
There was a problem hiding this comment.
저는 기존에 browserRouter로 감싸고 경로를 설정하는 식으로 썼는데 찾아보니 RouterProvider가 v6.4에 추가된 방식이군요..! 아래쪽 router 정의해주신 부분 보니 children 옵션에 중첩 라우팅을 추가해주신 부분까지 정말 잘 쓰신 것 같아요 👍❤️🔥
| import styled from "styled-components"; | ||
| import { friendListType } from "types/friendListType"; | ||
|
|
||
| type FavProfileProps = Pick<friendListType, "profile" | "user">; |
There was a problem hiding this comment.
기존 타입에서 특정 속성만 선택해서 새로운 타입을 만드는 Pick 새롭네요 !! 불필요한 속성 없이 객체를 가볍게 가져갈 수 있어서 좋은 것 같습니다
| export default function FavProfile(props: FavProfileProps) { | ||
| const { profile, user } = props; |
There was a problem hiding this comment.
함수 인자에서 바로 props를 구조 분해 할당하면 어떨까요!
| export default function FavProfile(props: FavProfileProps) { | |
| const { profile, user } = props; | |
| export default function FavProfile({ profile, user }: FavProfileProps) { |
| {isFavListOpen && ( | ||
| <> | ||
| <FavFriendList /> | ||
| </> | ||
| )} |
There was a problem hiding this comment.
요 빈 꺾쇠는 컴포넌트 하나여서 없애고 간결하게 써도 될 것 같습니당
|
|
||
| if (sender != "me") { | ||
| return ( | ||
| <FromContainer key={Math.random()}> |
There was a problem hiding this comment.
key값에 랜덤함수를 쓰신 이유가 있을까요? key는 컴포넌트의 고유성을 보장하기 위해 필요해서 난수보다는 uuid나 여기서는 해당 index, 시간 등을 조합하는 것도 방법일 것 같은데 아래 참고자료 한번 확인해보시면 좋을 것 같습니다!
|
|
||
| //다음 메세지랑 동시에 엔터엔터,,,를 했냐마냐 | ||
| const isSameTime = | ||
| nextMessage?.sender === sender && new Date(nextMessage?.time).getTime === new Date(time).getTime; |
There was a problem hiding this comment.
new Date().getTime 에서 getTime은 메서드라 괄호 ()를 붙여야 한다고 알고 있는데 이렇게 써도 무방한가요..!?
| <MessageTimeWrapper $showProfile={showProfile}> | ||
| <FromMessageBox showProfile={showProfile} message={message} /> |
There was a problem hiding this comment.
showProfile이 스타일에만 쓰이는 prop인 경우를 표기해주신 점 넘 세심하네요..💫
yyj0917
left a comment
There was a problem hiding this comment.
추후 코드리뷰를 더 이어나가고 싶을 정도로 많은 로직과 구현에도 완성도가 뛰어난 것 같습니다. 제가 보지 못 했던 설정들이나 컴포넌트 분리 방식 등에서 배울 점이 많아 얻어가는 게 많은 코드리뷰였습니다. 코드 구현하시느라 고생많으셨습니다! 시간되는대로 계속 들어와서 코드보고 리뷰남기겠습니다!
| const location = useLocation(); | ||
| const pathname = location.pathname; | ||
|
|
||
| useEffect(() => {}, [location]); |
There was a problem hiding this comment.
설정부분에서 처음 보는 게 많은 것 같아요! 기회되었을 때 어떤 설정을 하시고, 어떤 효과가 있는지 한번 찾아봐야겠습니다!!
There was a problem hiding this comment.
피그마 디자인 시스템을 themeprovider로 하신 부분이 인상깊습니다. 깔끔하고 직관적으로 보일 거 같아요
| {FRIEND_LIST.map((friend) => { | ||
| const { userId, user, statusMessage, profile } = friend; | ||
|
|
||
| return ( | ||
| <Link to={`/message/${userId}`}> | ||
| <FriendProfile key={userId} user={user} statusMessage={statusMessage} profile={profile} /> | ||
| </Link> | ||
| ); |
There was a problem hiding this comment.
map을 정말 잘 활용하시는 것 같아요! 이런 식으로 해당하는 friend로 계속 생성하는 것은 생각못해봤습니다
✨ 구현
구현링크호ㅇ홍
🌠 Key Questions
블로그에 정리해두었습니다
🌟 미션 목표
--
💎 필수 요건
🧩 PR Point
솔직히,,, 막 디자인적으로 어려웠던 부분도 많이 없었고, 로직도 복잡한 부분은 3주차 때 이미 쳐낸 뒤라,,, 😢
❤️ Router
필수 요건이었던 Router 관련입니다..
💚 Layout
사실 디자인을 쭊 보고 Layout을 좀더 확실히 했어야 했는데
얼레벌레 시간에 쫓기다 보니, 눈에 크게 보인 부분만 Layout으로 뺴놔서 나중에 좀 고생하긴 했지만,,,네,,
💙 채팅 리스트
마지막 메세지가 리스트에 보일 수 있도록 구현했습니다!
💛 zustand 사용
🔥 Truble Shooting
요거는 월욜 당일에 승완오빠가 알려줘서 알게 된 에러들인데,
일단 1번 이슈는 단톡방에 올라온
블로그를 참고하여 고쳤습니다.
문제는 2번 이슌데 일단 배포판에서 그런 거 발견해서 root 폴더 안에
vercel.json파일을 설정해 두었으나,아직 해결이 안된 상태이고
시간 이슈로,,,, 아직 깊이 고민해보지 못했습니다.... (오늘 4연강^^)
⏲️ 과제 후기
생각보다 시간이 없다고 생각했는데,,(수욜 오후 시작) 생각보다 빨리 끝났습니다..
아무래도 제가 다른 분들에 비해 디자인, 기능 모두 심플한 편이라 그런 것 같습니다!
그래도,,, 예쁘다고 생각해요,,, ㅎㅎㅎ
고새 머리가 많이 굳어서, next 들어가면서 한번 공부 날 잡고 해야할 것 같다는 생각도 들었습니다
얼레벌레 코딩은 그만 하구 싶어여,,ㅎㅎ