-
Notifications
You must be signed in to change notification settings - Fork 163
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
[장바구니 미션 Step 1] 센트(김영우) 미션 제출합니다. #147
Conversation
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
Co-authored-by: tkdrb12 <tkdrb15@gmail.com>
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.
안녕하세요 센트! 리뷰어 브콜입니다 ㅎㅎ
미션 진행하시느라 고생 많으셨습니다!
이번 미션은 크게 복잡한 로직이 없어 코멘트 드릴만한 점이 많지 않았습니다.
몇가지 내용 남겨두었으니 확인 부탁드려요!
이번 미션에서 recoil을 처음 사용해보았는데 지난 미션의 context api보다 훨씬 사용성이 좋다고 느꼈습니다. 그렇다 보니 현업에서도 recoil이 자주 사용되는지, 사용된다면 어느 부분에서 주로 다루는지 궁금해지게 되었습니다! 혹시 브콜이 recoil을 사용했던 경험을 공유해주실 수 있으신가요!!
회사에서 recoil을 쓰지 않아 크게 공유드릴만한 점은 없네요😅
가볍게 전역 상태를 정의하고 구독할 수 있다는 점이 매력적인 것 같은데, 앱의 규모가 커져감에 따라, 또 서버 state를 관리하기 시작함에 따라 어떤 사용성을 보여줄지는 저도 궁금합니다🧐
미션 진행하시면서 어떤 점이 좋고 어떤 아쉬움이 있는지 잘 정리해보시면 좋겠네요~
제가 만든 Header컴포넌트의 경우에는 따로 props를 넘겨 받지 않고, recoil을 통해 장바구니에 추가된 상품의 id를 담은 배열을 받아 개수를 렌더링 해주는 방식을 사용하고 있습니다. storybook을 통해 장바구니에 상품이 추가될 때 개수가 변경되어 렌더링 되는 모습을 보여주고자 했지만 storybook의 action에서는 넘겨 받는 props를 수정하는 것 만을 조작할 수 있는 것 같아 제가 의도한 모습을 보여주려면 어떻게 해야 할 지 궁금해졌습니다!
작성하신 Header 액션을 가진다기보단 특정 상태를 구독만 하고 있는 것으로 보여요
그럼 그 상태를 업데이트 해줄 수 있는 트리거만 옆에 살짝 달아놔도 되지 않을까요?
간단한 Counter 같은 거라도..
// decorator 예시
<BrowserRouter>
<RecoilRoot>
<GlobalStyle />
<Story />
<ProductCounter />
</RecoilRoot>
</BrowserRouter>
추가로 버그 및 이상한 UX가 있어 제보드립니다.
- storybook 배포 페이지가 안열리네요. netlify 권한 문제 인듯합니다.
- product가 추가되면 기존 Counter가 1로 초기화 됩니다.
2023-05-12.2.24.04.mov
- 다른 페이지에 다녀오면 각 product의 Counter 숫자가 전부 1로 초기화 됩니다.
- 상태를 유지해줄거라면 완전히 유지하고 아니라면 아예 초기화 하는 것이 나을 것 같습니다.
export const Counter = ({ removeItemFromCartList }: CounterProps) => { | ||
const { inputRef, handleDecrease, handleIncrease } = useCounterInput({ | ||
min: 0, | ||
handleMinValueExceeded: removeItemFromCartList, | ||
}); | ||
|
||
return ( | ||
<Style.Container> | ||
<Style.Button onClick={handleDecrease}>➖</Style.Button> | ||
<Style.Input value={1} ref={inputRef} type="number" readOnly /> | ||
<Style.Button onClick={handleIncrease}>➕</Style.Button> | ||
</Style.Container> | ||
); | ||
}; |
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.
상위 컴포넌트에서 이 counter의 value를 사용하고자 한다면 어떻게 얻을 수 있을까요?
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.
forwardRef를 통해 상위에서 ref를 넘겨 받아 해당 ref의 값을 변경시키는 방식으로 코드를 수정해 상위 컴포넌트에서 counter의 value를 사용하고자 할 때 넘겨준 ref를 통해 값에 접근할 수 있게 해보았습니다!
수정한 커밋: d149854
말씀 해주신 버그를 고쳐보기 위해 localStorage를 사용하는 커스텀 훅을 하나 추가해보았습니다! 또한 카운터의 값이 변경될 때마다 불필요한 리렌더링이 발생하지 않도록 하기 위해 굳이 카운터의 값을 상태로 두지 않고, ref를 통해 변경 가능하도록 하는 기존 기능을 그대로 가져가면서 ref의 값을 올리고 내릴 때 사용하는 메서드 내에 localStorage를 수정하는 로직을 추가해주었습니다!! |
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.
안녕하세요 센트!!
리뷰가 자꾸 늦는군요ㅠㅠ 죄송해요🥲
간단한 코멘트 남겨두었고 step2 진행하면서 확인하시면 되겠습니다!
한 카운터의 값이 변경될 때마다 불필요한 리렌더링이 발생하지 않도록 하기 위해 굳이 카운터의 값을 상태로 두지 않고, ref를 통해 변경 가능하도록 하는 기존 기능을 그대로 가져가면서 ref의 값을 올리고 내릴 때 사용하는 메서드 내에 localStorage를 수정하는 로직을 추가해주었습니다!!
모든 리렌더링이 불필요할지는 고민해보시면 좋겠습니다.
'상태를 관리'하고 '그 상태에 따라 어떤 view를 보여줘야할지 선언' 하는 것만으로 dom을 변경할 수 있는 것이 리액트의 장점 아니던가요~?
이를 위해 리액트는 리렌더(컴포넌트 호출) => vdom 생성 => real dom에 적용 하는 과정을 해주는 것이구요!
리액트의 선언적인 view render 방식은 각 컴포넌트 간 관심사를 분리하는데에도 크게 도움을 줍니다.
Counter 같은 경우 사실 사용처(부모)에서는 value와 value가 변하는 방식에 관심이 있지 Counter 내부에 어떤 엘리먼트가 있고 이를 어떻게 제어하는지는 관심을 둘 필요가 없다고 생각해요. controlled form 방식이었다면 이러한 관심사의 분리가 충분히 가능했을 것 같습니다.
그에 비해 각 사용처에서 forwarededRef를 넘겨 직접 dom을 제어하는 것에 어떤 이점이 있는지 궁금하네요🤔
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.
components 하위도 atom이 있을 자리는 아닌 것 같아요~
별개의 디렉터리로 관리해주세요
const handleIncrease = () => { | ||
inputRef.current?.stepUp(); | ||
|
||
if (increaseCallback) increaseCallback(); |
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.
if (increaseCallback) increaseCallback(); | |
increaseCallback?.(); |
} from '../utils/localStorage'; | ||
|
||
export const useLocalStorage = () => { | ||
const addNewCartId = (id: number) => { |
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.
useLocalStorage인데 storage보다는 cart 도메인에 대한 로직들 위주인 것 같군요.
안녕하세요 브콜!! 센트입니다😀😀 또 만나뵙게 됐네요! 이번 장바구니 미션 리뷰 잘 부탁드립니다👍
🔗 배포 페이지 | 스토리북 배포 페이지
🎯 기능 목록 + 학습 목표
구현한 기능 목록은 다음과 같습니다!
이번 미션을 통해 학습하고자 했던 개념은 다음과 같습니다!
recoil
을 통한 전역 상태 관리🧑💻 컴포넌트 구조
1. recoil 사용
이번 미션에서 recoil을 처음 사용해보았는데 지난 미션의 context api보다 훨씬 사용성이 좋다고 느꼈습니다. 그렇다 보니 현업에서도 recoil이 자주 사용되는지, 사용된다면 어느 부분에서 주로 다루는지 궁금해지게 되었습니다! 혹시 브콜이 recoil을 사용했던 경험을 공유해주실 수 있으신가요!!
2. props를 받지 않는 컴포넌트의 storybook action
이번에 UI 테스트 도구로 storybook을 선택하여 배포까지 진행을 해보았습니다! storybook의 action을 통해 여러 컴포넌트의 상호작용을 테스트해보고자 했는데 이때 문제가 발생하게 되었습니다. 제가 만든
Header
컴포넌트의 경우에는 따로 props를 넘겨 받지 않고, recoil을 통해 장바구니에 추가된 상품의 id를 담은 배열을 받아 개수를 렌더링 해주는 방식을 사용하고 있습니다. storybook을 통해 장바구니에 상품이 추가될 때 개수가 변경되어 렌더링 되는 모습을 보여주고자 했지만 storybook의 action에서는 넘겨 받는 props를 수정하는 것 만을 조작할 수 있는 것 같아 제가 의도한 모습을 보여주려면 어떻게 해야 할 지 궁금해졌습니다! 아직 storybook에 대한 숙련도가 높지 않아 다른 방식이 있는데 몰랐던 것인지, 혹은 이럴 때 사용하는 storybook 외적인 다른 방식이 있는지 궁금합니다!