특정 컴포넌트에 의존적인 Custom Hook을 많이 사용했었어요.
로직 분리를 위한거라면 어쩔 수 없지 않나? 라고 생각했는데,
크루들의 코드를 보고 느꼈습니다.
공통적으로 재사용 가능한 부분만을 발췌하면 Custom Hook을 의미있게 만들 수 있겠구나!
<첫 번째 예시>
import { useContext, useState } from 'react';
import { CardInfoDispatchContext } from '../context';
function useInputHandler(validator, { type, key, prevData }) {
const cardInfoDispatch = useContext(CardInfoDispatchContext);
const [errorMessage, setErrorMessage] = useState('');
const updateInputState = ({ name, value }) => {
setErrorMessage('');
try {
validator(value);
} catch (err) {
setErrorMessage(err.message);
return;
}
cardInfoDispatch({
type,
[key]: {
...prevData,
[name]: value,
},
});
};
return { errorMessage, setErrorMessage, updateInputState };
}
export default useInputHandler;
<두 번째 예시>
import { useEffect, useState } from "react";
const useReady = (state, validator, data) => {
const [ready, setReady] = useState(false);
useEffect(() => {
setReady(!validator(state, data));
}, [state, ready, validator, data]);
return [ready];
};
export default useReady;
Storybook은 우리의 앱 구동 시점과 다르게 완전 컴포넌트 독립적이예요.
따라서 Router, Redux등 다양한 외부 설정을 decorator로 설정해주어야합니다.
export const decorators = [
Story => (
<PathProvider>
<CardInfoProvider initialState={initialCardInfoState}>
<CardListProvider initialState={initialCardListState}>
<Story />
</CardListProvider>
</CardInfoProvider>
</PathProvider>
),
];
자 이제는 정말 안전하게 이전 상태를 사용합시다.
setIsShown(!isShown)
-------vs-------
setIsShown((prev) => !prev)
연산이 많아 useMemo를 활용은 인정합니다.
다만, 너무 많은 dependencies들이 있죠. 과연 사용하는게 맞을까요?
const isRequiredCompleted = useMemo(
() =>
cardCompany.name &&
cardCompany.hexColor &&
isCorrectCardNumber &&
cardDate.month &&
cardDate.year &&
cardCode.cvc.length === 3 &&
isCorrectPwd,
[cardCompany, cardDate, cardCode, isCorrectCardNumber, isCorrectPwd],
);
👉 차라리 아예 다른 모듈로 분리하고 가져다 쓰는 것이 좋을 수 있겠어요.
useLayoutEffect(() => {
if (targetRef.current) {
setDimensions({
width: targetRef.current.offsetWidth,
height: targetRef.current.offsetHeight,
});
}
}, []);
<CardBox onClick={handleClickBox} hexColor={hexColor} large={large} data-testid="card">
handleClickBox가 들어오지 않는다면 어떨까요?
handleClickBox 가 안들어올수 있으니 handleClickBox?.() 혹은 위 handleClickBox: () => void 0
이런식으로 작성해봅시다.
return (
<>
...
</>
)
Container니까 fragment 여도 괜찮은데, Container pattern은 HoC여야해요.
그러므로 다른 레이아웃이 추가적으로 붙어있으면 안됩니다! 왜 그럴까요?
레이아웃이 다른 page의 요소에 영향받지 않도록 wrapper를 만드는게 좋지 않을까요?
어떤 컴포넌트가 먼저 보이게할지 결정할 수 있어요.
쌓임 맥락 - CSS: Cascading Style Sheets | MDN (mozilla.org)
color를 정의해줄때 어떠한 "명사"는 비추합니다.
직관적으로 알 수 있도록 GRAY_001
컬러 팔레트 (GRAY_001... WHITE_001.. 등) 를 선 정의해두고
사용하는 color만
const usingColor = {
pageDefault : WHITE_100;
}
형태로 사용하게 되는 것이죠!
직접 체크해서 eslint 설정 파일로
옮길 필요가 있겠네요!
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
],
"overrides": [
{
"files": [
"**/*.stories.*"
],
"rules": {
"import/no-anonymous-default-export": "off"
}
}
]
},
const LOCAL_BASE_URL = 'http://localhost:4000';
const PRODUCT_BASE_URL = 'https://sisyphe-shopping-cart-server.herokuapp.com';
export const BASE_URL = process.env.NODE_ENV === 'production' ? PRODUCT_BASE_URL : LOCAL_BASE_URL;
👇 아래처럼 분리해보는 시도 멋있어요!
if (loading) return <Loading />;
if (error) return <RequestFail />;
return (
<div>
...
</div>
)
limit 개수와 페이지 id를 주면됩니다!
useFetch<Item[]>(`${BASE_URL}/itemList?_page=${id}&_limit=12`);
라우트가 매치되지 않았을 때 Not Found 페이지가 뜨는건 필수입니다!
로딩 / 실패 / 성공 케이스가 모두 조건문 기반으로 한 곳에 선언적인 코드의 의미가 떨어지는 것 같아요.
삼항 연산자가 가독성을 많이 망가트리고 있는 것 같은데 susepnse, error boundary에 대해서 한번 알아보시는것도 좋겠네요~
• 로딩, 실패, 성공 3가지 상태를 각각 Suspense
, ErrorBoundary
, 정상 UI로 나타내고, 선언적으로 작성한다.
React Suspense & ErrorBoundary 직접 만들기 (velog.io)
숫자를 지정하는 방식도 아래 palette처럼 낮은 숫자일 때 색상이 연하면 좋을 것 같습니다 🙁
Customizing Colors - Tailwind CSS
요건 조금 번외격인 TMI긴한데 레이아웃의 형태가 2개 이상인 경우 Outlet을 통해 해결해볼 수 있습니당.
공통 로직은 FlexColumnCenter처럼 분리해서 확장하는 형태로 사용합시다.
다른 스타일들은 아래처럼 객체? 느낌으로 가져가도 좋을 것 같아요.
const Styled = {
Wrapper: styled(FlexColumnCenter)`
height: calc(100vh - 200px);
gap: 40px;
`,
TextBox: styled.div`
text-align: center;
font-weight: 500;
font-size: 20px;
line-height: 1.4;
`,
};
page/ProductList/index.js
page/ProductList/UniqueProductComponent/index.js (해당 페이지에서만 사용하는 컴포넌트가 있다면 이런식으로?)
요렇게 재사용성이 없는 컴포넌트는 페이지 내부에 폴더별로 depth를 늘려나가면서 depth끼리 컴포넌트의 위계를 맞추면 되지 않을까 싶습니다 🤔
import * as Styled from './ErrorContainer.style';