4주차는 컴포넌트 설계패턴과 관련된 발제로 멘토님께서 진행해주셨다.
같은 로직인데 UI가 완전히 다른곳이 2곳이상 : Headless
props가 늘고, 사용처가 구조를 바꾸고 싶다 : Compound
open/value를 안에서도 밖에서도 : Controlled / UnControlled
어디서든 소환해야 (알림, 확인창) : Provider vs Singleton
단순 UI, props 3개 이하 : 그냥 props (패턴 x)
사실 위의 내용을 프론트엔드 공부를 하면서 들어보았지만 이번 발제를 통해 다시 생각해볼 수 있는 계기가 되었다.
특히 Headless! 나는 Headless 형태가 그냥 밑에처럼 컴포넌트 조각들로 제공해주는 방법만 알고 있었다.
<Select>
<Select.Trigger className="my-trigger">
상품 선택
</Select.Trigger>
<Select.Options className="my-options">
<Select.Option value="apple">Apple</Select.Option>
<Select.Option value="banana">Banana</Select.Option>
</Select.Options>
</Select>
근데 커스텀 훅으로 제공을 해주는 케이스도 있었다.
function useSelect() {
const [isOpen, setIsOpen] = useState(false);
const [value, setValue] = useState<string | null>(null);
return {
isOpen,
value,
triggerProps: {
onClick: () => setIsOpen((prev) => !prev),
"aria-expanded": isOpen,
},
getOptionProps: (option: string) => ({
onClick: () => {
setValue(option);
setIsOpen(false);
},
"aria-selected": value === option,
}),
};
}
const select = useSelect();
return (
<div>
<button {...select.triggerProps}>
{select.value ?? "선택하세요"}
</button>
{select.isOpen && (
<ul>
<li {...select.getOptionProps("apple")}>Apple</li>
<li {...select.getOptionProps("banana")}>Banana</li>
</ul>
)}
</div>
);
또 한가지 배운점은 SingleTon!
모듈에서 하나의 전역 인스턴스를 만들어 두고, React 컴포넌트가 아닌 곳에서도 직접 호출하는 방식이다.
type ToastListener = (message: string) => void;
const listeners = new Set<ToastListener>();
export const toast = {
success(message: string) {
listeners.forEach((listener) => listener(message));
},
subscribe(listener: ToastListener) {
listeners.add(listener);
return () => {
listeners.delete(listener);
};
},
};
Provider 방식과 다른점은 Provider는 리액트 트리 내에서 Provider 내부에서만 사용을 할 수 있는데 SingleTon은 리액트 트리와 무관없이 다음과 같이 호출을 할 수 있다.
export async function request<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
toast.error("요청 처리에 실패했습니다.");
throw new Error("Request failed");
}
return response.json();
}
다시 한번 컴포넌트 설계 패턴을 정리하고 필요할 때 적절한 방법을 선택하는게 중요하겠다고 생각을 하였다.