React Components
@teamsparta/react에서 제공하는 선언적 유틸리티 컴포넌트 모음입니다.
When
조건부 렌더링을 선언적으로 처리하는 컴포넌트입니다.
API
typescript
type Condition = boolean | (() => boolean);
interface WhenProps {
condition: Condition;
fallback?: ReactNode;
children: ReactNode;
}
function When(props: PropsWithChildren<WhenProps>): ReactElement;사용 예시
tsx
<When condition={isLoggedIn} fallback={<LoginButton />}>
<Dashboard />
</When>
// 함수 형태의 조건도 가능
<When condition={() => items.length > 0}>
<ItemList items={items} />
</When>SwitchCase
값에 따른 분기 렌더링을 선언적으로 처리하는 컴포넌트입니다.
API
typescript
type CaseKey<Value> =
| (Value extends boolean ? 'true' | 'false' : never)
| (Value extends PropertyKey ? Value : never);
interface SwitchCaseProps<Value> {
value: Value | null | undefined;
cases: Partial<Record<CaseKey<Value>, ReactNode>>;
defaultComponent?: ReactNode;
}
function SwitchCase<Value>(props: SwitchCaseProps<Value>): ReactElement;사용 예시
tsx
<SwitchCase
value={status}
cases={{
loading: <Spinner />,
success: <SuccessMessage />,
error: <ErrorMessage />,
}}
defaultComponent={<Placeholder />}
/>
// boolean 값도 지원
<SwitchCase
value={isActive}
cases={{
true: <ActiveBadge />,
false: <InactiveBadge />,
}}
/>Separated
자식 요소 사이에 구분자를 삽입하는 컴포넌트입니다.
API
typescript
interface SeparatedProps {
with: ReactNode; // 구분자 요소
first?: boolean; // 첫 번째 요소 앞에도 구분자 삽입 (기본값: false)
last?: boolean; // 마지막 요소 뒤에도 구분자 삽입 (기본값: false)
children: ReactNode;
}
function Separated(props: PropsWithChildren<SeparatedProps>): ReactElement;사용 예시
tsx
<Separated with={<hr />}>
<Section1 />
<Section2 />
<Section3 />
</Separated>
// 결과: Section1 <hr/> Section2 <hr/> Section3
<Separated with={<Divider />} first last>
{items.map(item => <Item key={item.id} {...item} />)}
</Separated>Spacer
요소 사이에 공간을 추가하는 폴리모픽 컴포넌트입니다.
API
typescript
interface SpacerProps<T extends ElementType = 'div'> {
as?: T; // 기본값: 'div'
orientation?: 'vertical' | 'horizontal'; // 기본값: 'vertical'
size?: string | number; // 기본값: '1rem', 숫자는 px로 변환
style?: CSSProperties;
// + T의 모든 props
}
const Spacer: <T extends ElementType = 'div'>(props: SpacerProps<T>) => ReactNode;사용 예시
tsx
<div>
<Header />
<Spacer size={24} />
<Content />
<Spacer size="2rem" />
<Footer />
</div>
// 수평 간격
<div style={{ display: 'flex' }}>
<LeftPanel />
<Spacer orientation="horizontal" size={16} />
<RightPanel />
</div>Delay
지정된 시간 후에 컨텐츠를 렌더링하는 컴포넌트입니다. Suspense fallback의 깜빡임 방지에 유용합니다.
API
typescript
// DelayProps는 패키지에서 export됨
export type DelayProps =
| {
ms?: number; // 기본값: 0
fallback?: never;
children: (props: { isDelayed: boolean }) => ReactNode;
}
| {
ms?: number;
fallback?: ReactNode; // 지연 중 보여줄 컨텐츠
children?: ReactNode; // 지연 후 보여줄 컨텐츠
};
function Delay(props: DelayProps): ReactElement;사용 예시
tsx
// 기본 사용: 300ms 후에 스피너 표시
<Suspense fallback={<Delay ms={300} fallback={null}><Spinner /></Delay>}>
<AsyncComponent />
</Suspense>
// render prop 패턴
<Delay ms={500}>
{({ isDelayed }) => isDelayed ? <Content /> : <Skeleton />}
</Delay>InView
Intersection Observer를 래핑한 컴포넌트로, 자식 요소의 가시성을 관찰합니다.
API
typescript
interface InViewProps extends UseIntersectionObserverProps {
children?: ReactElement | ReactElement[];
}
function InView(props: InViewProps): ReactElement | null;사용 예시
tsx
<InView
onEnter={() => trackImpression('banner')}
onLeave={() => console.log('배너가 화면에서 사라짐')}
threshold={0.5}
>
<div>배너 영역</div>
</InView>
// 한 번만 감지
<InView onEnter={() => loadMore()} unobserveOnLeave>
<div>무한 스크롤 트리거</div>
</InView>Suspense
클라이언트 전용 모드를 지원하는 Suspense 래퍼 컴포넌트입니다.
API
typescript
interface SuspenseProps extends React.SuspenseProps {
clientOnly?: boolean; // true이면 서버에서는 fallback만 렌더링
}
function Suspense(props: SuspenseProps): ReactElement;사용 예시
tsx
// 기본 사용
<Suspense fallback={<Spinner />}>
<AsyncComponent />
</Suspense>
// 클라이언트 전용 (SSR에서 fallback 표시)
<Suspense clientOnly fallback={<Skeleton />}>
<BrowserOnlyComponent />
</Suspense>Portal
React createPortal을 편리하게 사용할 수 있는 컴포넌트입니다.
API
typescript
// PortalProps는 패키지에서 export됨
export interface PortalProps {
children: ReactNode;
container?: Element | DocumentFragment | null; // 기본값: document.body
}
function Portal(props: PortalProps): ReactElement | null;사용 예시
tsx
// document.body에 렌더링
<Portal>
<div className="modal">모달 내용</div>
</Portal>
// 특정 컨테이너에 렌더링
<Portal container={document.getElementById('modal-root')}>
<Tooltip>도움말</Tooltip>
</Portal>