React Hooks
@teamsparta/react에서 제공하는 커스텀 훅 모음입니다.
useBoolean
boolean 상태값을 관리하기 위한 훅입니다.
API
function useBoolean(defaultValue?: boolean): {
value: boolean;
setTrue: () => void;
setFalse: () => void;
toggle: () => void;
};사용 예시
const { value, setTrue, setFalse, toggle } = useBoolean(false);
return (
<div>
<p>{value.toString()}</p>
<button onClick={setTrue}>True</button>
<button onClick={setFalse}>False</button>
<button onClick={toggle}>Toggle</button>
</div>
);useToggle
boolean 상태를 간편하게 토글하는 훅입니다. useReducer 기반으로 동작합니다.
API
function useToggle(defaultValue?: boolean): [state: boolean, toggle: () => void];사용 예시
const [isOpen, toggle] = useToggle();
return (
<div>
<p>{isOpen ? '열림' : '닫힘'}</p>
<button onClick={toggle}>토글</button>
</div>
);useClickAway
특정 요소 외부 클릭을 감지하는 훅입니다.
API
function useClickAway<T extends HTMLElement = HTMLElement>(
callback: () => void
): React.RefObject<T>;사용 예시
const ref = useClickAway<HTMLDivElement>(() => {
console.log('외부 클릭!');
});
return <div ref={ref}>이 영역 외부 클릭 시 콜백 실행</div>;useDebounce
입력값의 변경을 지정된 시간만큼 지연시키는 훅입니다.
API
function useDebounce<T>(value: T, delay?: number): T;
// delay 기본값: 500 (ms)사용 예시
const [value, setValue] = useState('');
const debouncedValue = useDebounce(value, 500);
// debouncedValue는 value 변경 후 500ms 뒤에 업데이트useThrottle
입력값을 지정된 시간 동안 제한된 속도로 반환하는 훅입니다.
API
function useThrottle<T>(value: T, delay?: number): T;
// delay 기본값: 1000 (ms)사용 예시
const [value, setValue] = useState('');
const throttledValue = useThrottle(value, 1000);usePreserveCallback
콜백 함수의 레퍼런스를 보존하여 불필요한 리렌더링을 방지하는 훅입니다.
API
function usePreserveCallback<T extends (...args: any[]) => any>(
callback: T
): (...args: Parameters<T>) => void;사용 예시
const useSomething = (someCallback: () => void) => {
const callback = usePreserveCallback(someCallback);
useEffect(() => {
callback();
}, [callback]); // 콜백 레퍼런스가 변경되지 않아 불필요한 재실행 방지
};useTimeout
지정된 시간 후 콜백을 실행하는 훅입니다.
API
function useTimeout(props: {
callback: () => void;
delay?: number; // 기본값: 0 (ms)
}): void;사용 예시
useTimeout({
callback: () => setTitle('로딩 중...'),
delay: 2000,
});useInterval
주기적으로 콜백을 실행하는 타이머 훅입니다.
API
interface UseIntervalProps {
onTick: () => void;
delay: number; // 밀리초, 0보다 커야 함
disabled?: boolean; // 기본값: false
immediate?: boolean; // 기본값: false, true면 즉시 첫 실행
}
function useInterval(props: UseIntervalProps): void;사용 예시
useInterval({
onTick: () => fetchData(),
delay: 5000,
immediate: true,
});useTimer
감소하는 카운트다운 타이머 훅입니다.
API
interface UseTimerProps {
expireTime?: number;
onStart?: () => void;
onReset?: () => void;
onPause?: () => void;
onExpire?: () => void;
syncTime?: boolean; // 기본값: false
autoStart?: boolean; // 기본값: false
interval?: number; // 기본값: 1000 (ms)
decrementValue?: number; // 기본값: 1
}
function useTimer(props: UseTimerProps): {
isRunning: boolean;
time: number;
start: () => void;
reset: () => void;
pause: () => void;
};사용 예시
const { isRunning, time, start, reset, pause } = useTimer({
expireTime: 60,
autoStart: true,
onExpire: () => alert('시간 종료!'),
});
return <div>{time}초 남음</div>;useStopwatch
증가하는 스톱워치 훅입니다.
API
interface UseStopwatchProps {
initialTime?: number;
onStart?: () => void;
onStop?: () => void;
onPause?: () => void;
onTick?: (time: number) => void;
syncTime?: boolean; // 기본값: false
autoStart?: boolean; // 기본값: false
}
function useStopwatch(props: UseStopwatchProps): {
isRunning: boolean;
time: number;
start: () => void;
stop: () => void;
pause: () => void;
};사용 예시
const { isRunning, time, start, stop, pause } = useStopwatch({
initialTime: 0,
autoStart: true,
onTick: (t) => console.log(`${t}초 경과`),
});useDocumentVisibility
문서의 가시성 상태(탭 전환) 변경을 감지하는 훅입니다.
API
function useDocumentVisibility(props: {
onVisibilityChange: (visibility: 'visible' | 'hidden') => void;
}): void;사용 예시
useDocumentVisibility({
onVisibilityChange: (visibility) => {
if (visibility === 'hidden') pauseVideo();
else playVideo();
},
});useIntersectionObserver
Intersection Observer API를 사용하여 요소의 가시성 변화를 관찰하는 훅입니다.
API
interface UseIntersectionObserverProps {
root?: IntersectionObserverInit['root'];
rootMargin?: IntersectionObserverInit['rootMargin'];
threshold?: IntersectionObserverInit['threshold'];
onChange?: (entry: IntersectionObserverEntry) => void;
onEnter?: () => void;
onLeave?: () => void;
/** @deprecated unobserveOnLeave 사용 권장 */
unobserveOnEnter?: boolean;
unobserveOnLeave?: boolean;
}
function useIntersectionObserver<E extends HTMLElement>(
props: UseIntersectionObserverProps
): {
ref: React.RefObject<E>;
observe: () => void;
unobserve: () => void;
};사용 예시
const { ref } = useIntersectionObserver<HTMLDivElement>({
onEnter: () => console.log('요소가 화면에 나타남'),
onLeave: () => console.log('요소가 화면에서 사라짐'),
threshold: 0.5,
});
return <div ref={ref}>관찰 대상</div>;useResizeObserver
Resize Observer API를 사용하여 요소의 크기 변화를 관찰하는 훅입니다.
API
function useResizeObserver<E extends HTMLElement = HTMLDivElement>(props: {
onResize?: (entry: ResizeObserverEntry) => void;
}): React.RefObject<E>;사용 예시
const containerRef = useResizeObserver({
onResize: (entry) => {
console.log('너비:', entry.contentRect.width);
},
});
return <div ref={containerRef}>크기가 변하는 요소</div>;useHover
마우스 hover 상태를 감지하는 훅입니다.
API
interface UseHoverProps {
onHoverStart?: () => void;
onHoverEnd?: () => void;
}
function useHover<T extends HTMLElement>(
props?: UseHoverProps
): {
targetRef: React.RefObject<T>;
isHovered: boolean;
};사용 예시
const { targetRef, isHovered } = useHover<HTMLDivElement>({
onHoverStart: () => console.log('hover 시작'),
onHoverEnd: () => console.log('hover 종료'),
});
return (
<div ref={targetRef} style={{ background: isHovered ? 'blue' : 'white' }}>
Hover me
</div>
);useMediaQuery
미디어 쿼리의 일치 여부를 구독하는 훅입니다. useSyncExternalStore 기반으로 SSR 안전합니다.
API
type MediaQuery = ScreenSizeQuery | DisplayOrientationQuery | DeviceInputQuery
| DisplayQualityQuery | UserPreferenceQuery | EnvironmentQuery | (string & {});
function useMediaQuery(query: MediaQuery, defaultValue?: boolean): boolean;참고:
useMediaQuery.ts모듈에는media유틸리티 객체와 편의 훅(useMinWidth,useMaxWidth,useBetweenWidth등)이 함께 정의되어 있지만, 패키지 진입점(@teamsparta/react)에서는useMediaQuery만 re-export됩니다. 편의 훅이 필요한 경우useMediaQuery와 직접 미디어 쿼리 문자열을 조합하여 사용하세요.
사용 예시
const isMobile = useMediaQuery('(max-width: 768px)', false);
const isDarkMode = useMediaQuery('(prefers-color-scheme: dark)', false);useLockBodyScroll
페이지의 스크롤을 제한하는 훅입니다. 모달이나 팝업에서 사용합니다.
API
interface ScrollLockOptions {
isLocked?: boolean; // 기본값: false
preventShift?: boolean; // 기본값: true, 스크롤바 너비만큼 패딩 추가
preventTouchScroll?: boolean; // 기본값: true, iOS Safari 터치 스크롤 방지
}
function useLockBodyScroll(options?: ScrollLockOptions): void;사용 예시
function Modal({ isOpen }) {
useLockBodyScroll({ isLocked: isOpen });
return isOpen ? <div>Modal Content</div> : null;
}useIsClient
현재 환경이 클라이언트(브라우저)인지 확인하는 훅입니다. useSyncExternalStore 기반으로 SSR 안전합니다.
API
function useIsClient(): boolean;
// 서버: false, 클라이언트: true사용 예시
const isClient = useIsClient();
if (!isClient) return <div>Loading...</div>;
return <div>클라이언트에서만 보이는 컨텐츠</div>;useIsomorphicLayoutEffect
SSR과 클라이언트 환경에서 모두 안전하게 동작하는 useLayoutEffect입니다.
API
const useIsomorphicLayoutEffect: typeof useLayoutEffect;
// 클라이언트: useLayoutEffect, 서버: useEffect사용 예시
useIsomorphicLayoutEffect(() => {
// DOM 측정이 필요한 코드
}, []);useFunnel
퍼널(단계별 흐름) UI를 관리하는 훅입니다.
API
type NonEmptyArray<T> = readonly [T, ...T[]];
function useFunnel<Steps extends NonEmptyArray<string>>(props: {
steps: Steps;
initialStep?: Steps[number]; // 기본값: steps[0]
tracingExternalStep?: boolean; // 기본값: false
externalStep?: Steps[number];
}): readonly [
FunnelComponent & { Step: StepComponent },
React.Dispatch<React.SetStateAction<Steps[number]>>,
Steps[number]
];
// FunnelComponent props (step은 자동 주입)
type FunnelProps<Steps> = {
children: ReactElement<StepProps<Steps>>[] | ReactElement<StepProps<Steps>>;
};
// Step props
type StepProps<Steps> = {
name: Steps[number];
onEnter?: () => void;
children: ReactNode;
};사용 예시
const [Funnel, setStep, currentStep] = useFunnel({
steps: ['info', 'address', 'confirm'] as const,
initialStep: 'info',
});
return (
<Funnel>
<Funnel.Step name="info">
<InfoForm onNext={() => setStep('address')} />
</Funnel.Step>
<Funnel.Step name="address">
<AddressForm onNext={() => setStep('confirm')} />
</Funnel.Step>
<Funnel.Step name="confirm">
<ConfirmPage />
</Funnel.Step>
</Funnel>
);