자주쓰는 React Hook 정리
React에서 가장 많이 쓰이는 Hook & 특징 정리
useState — 상태 관리의 시작점
React에서 가장 기본이 되는 Hook.
컴포넌트가 가지고 있어야 하는 “변하는 값”을 저장.
- 렌더링에 영향을 줌.
- setState가 호출되면 컴포넌트 재렌더링.
- primitive, object, array 전부 관리 가능.
문제 상황
- 유저가 버튼을 누를 때 화면에 숫자가 올라가야 한다.
- input에 텍스트 입력하면 그 내용이 실시간으로 화면에 반영돼야 한다.
즉, “값이 바뀌면 화면도 바뀌어야 하는 상황”.
예시
- 좋아요 버튼 누르면 숫자 증가
- 입력창에 입력하면 실시간으로 보여주기
- 모달 열기/닫기 상태
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen(true)}>모달 열기</button>
{open && <Modal />}
</>
);
=> UI를 직접 바꾸는 ‘상태 값’이 필요할 때 useState.
useEffect — 부수효과(side effect) 처리
렌더링 후 실행되는 로직들 처리.
데이터 fetch, API 호출, 이벤트 등록/해제 등.
- dependency 배열이 중요.
- [] → 마운트 시 딱 한 번 실행
- [변수] → 해당 값이 바뀔 때 실행
- cleanup 함수 리턴 가능 (이벤트 제거 등)
문제 상황
React는 렌더링에만 집중하는데,
일부 로직은 “렌더링 외부에서 발생”함.
예를 들어:
- 페이지에 처음 들어올 때 API 호출해야 함
- 스크롤 이벤트 등록해야 함
- setTimeout, setInterval 설정해야 함
- 언마운트 시 정리(cleanup) 필요함
이건 React 컴포넌트의 “자연스러운 렌더링 흐름”과 별개.
예시
페이지가 켜질 때 API를 불러와야 할 때
useEffect(() => {
fetch("/api/products")
.then(res => res.json())
.then(console.log);
}, []);
컴포넌트가 사라질 때 이벤트 정리
useEffect(() => {
const handler = () => console.log("scroll!");
window.addEventListener("scroll", handler);
return () => window.removeEventListener("scroll", handler);
}, []);
=> “렌더링 외부에서 발생하는 일”을 실행할 때 사용.
useRef — 값 기억 + DOM 접근
렌더링과 상관없이 변경 가능한 값을 저장하는 Hook.
DOM 요소 직접 접근할 때도 사용.
- 값 변경해도 렌더링 트리거 없음.
- 상태처럼 변하지만 화면에는 영향을 주지 않는 경우 사용.
문제 상황
- 특정 DOM 요소에 직접 접근해야 한다.
- 값은 저장해야 하는데 바뀌어도 렌더링되면 안 된다.
- 예: input 자동 focus, 스크롤 제어, interval id 저장 등
예시
1) input에 포커스 주기
const ref = useRef<HTMLInputElement>(null);
useEffect(() => {
ref.current?.focus();
}, []);
2) 렌더링과 관계없이 값 저장하기
(예: 이전 값 기억)
const renderCount = useRef(0);
renderCount.current += 1;
useContext — 전역 상태 공유
props drilling 없이 상태를 여러 컴포넌트에서 공유할 때 사용.
- 전역 변수 느낌 (Redux보다 간단).
- 단독 사용보단 useReducer + useContext 조합이 많음.
문제 상황
- 부모 → 자식 → 자식 → 자식 → 자식
늘어지는 props 전달 (props drilling) 때문에 코드 복잡.
전역으로 공유해야 하는 상태가 있을 때 필요.
예시
- 로그인 정보 전역 공유
- 다크모드 / 라이트모드 전역 적용
- 장바구니 같은 앱 전체 데이터 공유
const ThemeContext = createContext("light");
const App = () => (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
);
const Child = () => {
const theme = useContext(ThemeContext);
return <div>현재 테마: {theme}</div>;
};
=> 전역적인 값을 여러 컴포넌트가 공유해야 할 때.
useReducer — useState 강화판
복잡한 상태 관리 로직을 reducer 패턴으로 관리
- Redux의 로직과 거의 동일.
- 상태 변화 규칙이 명확해짐.
- useContext와 함께 전역 상태로 자주 활용.
문제 상황
useState로 관리하려니 상태 변화 규칙이 너무 복잡함.
예:
- 장바구니에 상품 추가 / 삭제 / 수량 증가 / 감소
- 폼 입력값 여러 개 관리
- 한 값이 바뀌면 다른 값도 영향을 받는 구조
예시
- 장바구니 로직이 복잡할 때
function reducer(state, action) {
switch(action.type) {
case "add":
return [...state, action.item];
case "remove":
return state.filter(i => i.id !== action.id);
default:
return state;
}
}
const [cart, dispatch] = useReducer(reducer, []);
------------------------------------------------------
예시 2)
type Action = { type: "increment" } | { type: "decrement" };
function reducer(state: number, action: Action) {
switch (action.type) {
case "increment": return state + 1;
case "decrement": return state - 1;
default: return state;
}
}
const [count, dispatch] = useReducer(reducer, 0);
=> 상태 변화를 “규칙”으로 관리해야 할 때 useReducer 사용.
useMemo — 값 계산 최적화
무거운 계산이 필요할 때, 값 캐싱.
- dependency가 바뀌기 전까지 기존 값 재사용.
- 성능 최적화 시 사용.
문제 상황
렌더링될 때마다 무거운 계산을 다시 하면 성능 저하.
예:
- 필터링된 리스트 계산
- 정렬
- 큰 배열에서 찾아오는 연산
- CPU 부담 큰 작업
예시
const expensiveValue = useMemo(() => {
return heavyCalc(data);
}, [data]);
=> 값 계산 비용이 클 때 캐싱용으로 사용.
useCallback — 함수 최적화
불필요한 재렌더링 막고 싶을 때 함수 자체를 메모이제이션.
- 자식 컴포넌트에 함수 props 넘길 때 자주 사용.
- useMemo가 “값” 메모, useCallback은 “함수” 메모.
문제 상황
자식 컴포넌트에 함수 props를 넘기면 “함수 자체가 새로 생성”돼서
자식이 불필요하게 리렌더됨.
이를 방지하고 싶을 때.
예시
const handleClick = useCallback(() => {
console.log("clicked");
}, []);
=> 동일한 함수를 유지해야 최적화가 되는 상황에서 사용.
useLayoutEffect — 렌더 직후 실행
useEffect와 유사하지만 DOM이 그려지기 전에 동기적으로 실행됨.
- 화면 깜빡임(flicker) 방지할 때 사용.
- useEffect보다 실행 시점이 빠름.
문제 상황
DOM이 그려지기 전에 측정/조작해야 하는 상황.
예:
- DOM 크기를 계산하고 그 값을 이용해야 하는 컴포넌트
- 화면 깜빡임 방지
예시
useLayoutEffect(() => {
const width = ref.current?.offsetWidth;
console.log("layout width: ", width);
});
=> 화면이 깜빡이는 걸 막고 싶을 때 useLayoutEffect.
useTransition — UI 느림 방지 (React 18)
비동기 업데이트를 “긴급하지 않은 업데이트”로 처리.
- 무거운 렌더링을 사용자 인터랙션과 분리.
- 로딩 표시도 자동으로 처리 가능.
문제 상황
무거운 작업 때문에 입력 딜레이가 생기는 경우.
예:
- 검색창에 글자 입력할 때 리스트 필터링이 너무 무겁다.
→ UI가 끊긴다.
예시
const [isPending, startTransition] = useTransition();
startTransition(() => {
setBigListData(newData);
});
=> 긴 작업을 “비긴급 업데이트”로 바꿔 UI 렉 방지.
커스텀 훅 (Custom Hook)
Hook을 조합해서 재사용 가능한 로직을 만들 때.
예시 — useFetch
const useFetch = <T,>(url: string) => {
const [data, setData] = useState<T | null>(null);
useEffect(() => {
fetch(url).then(res => res.json()).then(setData);
}, [url]);
return data;
};