2024. 12. 16. 11:24ㆍ카테고리 없음
요약
기본 상태 및 생애주기 훅 | useState | 함수형 컴포넌트에서 로컬 상태를 관리합니다. |
useEffect | 함수형 컴포넌트에서 부수 효과를 처리합니다 (예: 데이터 가져오기, 구독). | |
useContext | React 컨텍스트에서 값을 소비합니다. | |
참조 및 DOM 훅 | useRef | 렌더링 간에 지속되는 가변 참조 객체를 생성합니다. |
useLayoutEffect | useEffect와 비슷하지만, DOM 변경 후 동기적으로 실행됩니다. | |
성능 및 최적화 훅 | useMemo | 값을 메모이제이션하여 렌더링할 때 비싼 계산을 피합니다. |
useCallback | 함수가 불필요하게 새로 생성되는 것을 방지합니다. | |
useDeferredValue | 값 업데이트를 지연시켜 UI 차단을 방지합니다. | |
useTransition | 업데이트를 "전환"으로 표시하여 UI를 차단하지 않도록 지연시킬 수 있습니다. | |
고급 상태 관리 훅 | useReducer | 복잡한 상태 로직이나 이전 상태에 의존하는 상태 관리에 유용합니다. |
useImperativeHandle | 컴포넌트에서 ref를 사용할 때 노출되는 인스턴스 값을 커스터마이징합니다. | |
기타 | useSyncExternalStore | 외부 상태 구독 |
useId | 고유한 ID 생성 (주로 접근성 관련) |
+--------------------------------------+
| React 훅 |
+--------------------------------------+
/ | \
/ | \
+------------+------------+------------+-----------------+
| | | | |
+-----+-----+ +---+--------+ +---------+ +--------------+ +---------------+
| useState | | useEffect | | useContext| | useRef | | useReducer |
+-----------+ +------------+ +-----------+ +-------------+ +---------------+
/ \
+-----+-----+ +-----+------+
| useMemo | | useCallback |
+-----------+ +-------------+
/ \
+--------+--------+ +------++--------+
| useLayoutEffect | | useTransition |
+------------------+ +---------------+
|
+-------+--------+
| useDeferredValue|
+-----------------+
1. 기본 상태 및 생애주기 훅
1.1 useState (상태 관리)
목적 | 함수형 컴포넌트에서 상태를 관리 |
사용 시점 | 함수형 컴포넌트에서 상태를 관리할 때 |
반환 값 | 상태와 상태를 갱신하는 함수가 담긴 배열 |
주요 특성 | 상태 변경 시 컴포넌트가 리렌더링됨. |
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
export default Counter;
useState는 상태를 관리하는 데 사용됩니다. 버튼을 클릭할 때마다 상태가 변경됩니다.
1.2 useEffect (부수 효과)
목적 | 부수 효과(예: 데이터 fetching, DOM 업데이트, 구독)를 처리 |
사용 시점 | 컴포넌트 렌더링 후 부수 효과를 처리할 때 |
반환 값 | undefined 또는 클린업 함수(선택적) |
주요 특성 | 렌더링 후 실행되며, 클린업 함수 지원. |
useEffect(() => {
// 랜더링 될 때마다
console.log('effect');
return () => {
// 컴포넌트가 언마운트 될 때
console.log('cleanup');
}
}, [name]); // name 값이 업데이트 될 때만 실행하려면 값 입력
import React, { useState, useEffect } from 'react';
function Timer() {
const [time, setTime] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setTime(prevTime => prevTime + 1);
}, 1000);
// Cleanup 함수
return () => clearInterval(interval);
}, []); // 컴포넌트가 마운트될 때만 실행
return <div>시간: {time}초</div>;
}
export default Timer;
useEffect는 컴포넌트가 렌더링된 후 부수 효과를 처리합니다. 위 예제에서는 타이머를 설정합니다.
1.3 useContext (Context 사용)
목적 | React Context 값을 소비 |
사용 시점 | Context에서 제공하는 값을 소비할 때 |
반환 값 | 현재 Context 값 |
주요 특성 | 컴포넌트 트리 전체에서 값을 공유. |
import React, { useState, useContext, createContext } from 'react';
// Context 생성
const ThemeContext = createContext();
function ThemedComponent() {
const theme = useContext(ThemeContext);
return <div style={{ background: theme.background, color: theme.color }}>테마: {theme.name}</div>;
}
function App() {
const theme = {
name: '다크 모드',
background: '#333',
color: '#fff',
};
return (
<ThemeContext.Provider value={theme}>
<ThemedComponent />
</ThemeContext.Provider>
);
}
export default App;
useContext는 React 컨텍스트에서 값을 가져오는 데 사용됩니다. ThemeContext에서 테마를 소비하고 이를 컴포넌트에 반영합니다.
2.참조 및 DOM 훅
2.1 useRef (참조 관리)
목적 | DOM 요소나 변수를 위한 참조를 관리 |
사용 시점 | DOM 요소에 접근하거나 렌더 간 값을 저장할 때 |
반환 값 | current 속성을 가진 ref 객체 |
주요 특성 | 리렌더링을 트리거하지 않음. |
import React, { useRef } from 'react';
function FocusInput() {
const inputRef = useRef();
const handleFocus = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleFocus}>입력란에 포커스</button>
</div>
);
}
export default FocusInput;
useRef는 DOM 요소에 대한 참조를 관리합니다. 위 예제에서는 버튼을 클릭하면 입력란에 포커스를 설정합니다.
2.2 useLayoutEffect (레이아웃 후 효과)
목적 | DOM 업데이트 후 동기적으로 부수 효과를 실행 |
사용 시점 | DOM을 읽거나 수정해야 할 때 |
반환 값 | undefined 또는 클린업 함수(선택적) |
주요 특성 | DOM 업데이트 후 화면에 그려지기 전에 실행됨. |
import React, { useLayoutEffect, useState } from 'react';
function LayoutEffectExample() {
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // 처음 렌더링될 때만 실행
return <div>화면 너비: {width}px</div>;
}
export default LayoutEffectExample;
useLayoutEffect는 레이아웃이 변경된 후 DOM을 동기적으로 업데이트할 때 사용합니다.
3.성능 및 최적화 훅
3.1 useMemo (값 메모이제이션)
목적 | 값의 계산을 메모이제이션하여 불필요한 계산을 방지 |
사용 시점 | 비싼 계산을 최적화하려 할 때 |
반환 값 | 메모이제이션된 값 |
주요 특성 | 의존성 배열이 변경될 때만 재계산됨. |
import React, { useMemo, useState } from 'react';
function ExpensiveComputation({ num }) {
const computeExpensiveValue = (num) => {
console.log('계산 중...');
return num * 2;
};
const memoizedValue = useMemo(() => computeExpensiveValue(num), [num]);
return <div>계산된 값: {memoizedValue}</div>;
}
function App() {
const [num, setNum] = useState(0);
return (
<div>
<ExpensiveComputation num={num} />
<button onClick={() => setNum(num + 1)}>증가</button>
</div>
);
}
export default App;
useMemo는 계산이 많은 값이 변경되었을 때만 재계산되도록 최적화합니다.
3.2 useCallback (함수 메모이제이션)
목적 | 함수를 메모이제이션하여 불필요한 재생성을 방지 |
사용 시점 | 함수를 props로 전달하고 불필요한 리렌더링을 방지할 때 |
반환 값 | 메모이제이션된 함수 |
주요 특성 | 함수의 재생성을 방지함. |
import React, { useCallback, useState } from 'react';
function Button({ onClick }) {
return <button onClick={onClick}>클릭</button>;
}
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]); // count가 변경될 때만 함수 재생성
return (
<div>
<Button onClick={handleClick} />
<p>클릭 횟수: {count}</p>
</div>
);
}
export default App;
useCallback은 함수가 불필요하게 재생성되는 것을 방지하여 성능을 최적화합니다.
3.3 useDeferredValue (UI 또는 필터링 지연되는 값 처리)
목적 | 덜 중요한 UI 업데이트를 지연시켜 렌더링 최적화 |
사용 시점 | 중요한 업데이트를 먼저 처리하고 덜 중요한 업데이트는 지연시킬 때 |
반환 값 | 지연된 값 |
주요 특성 | UI 업데이트의 우선순위를 조절. (React 18 기능) |
import React, { useState, useDeferredValue } from 'react';
function Search() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query); // 지연된 값
return (
<div>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="검색어 입력"
/>
<p>입력된 값: {deferredQuery}</p> {/* 지연된 입력값 */}
</div>
);
}
export default Search;
useDeferredValue는 UI 업데이트를 지연시켜 우선순위가 높은 업데이트가 먼저 처리되도록 합니다. 주로 입력 필드와 같은 비동기적인 상황에서 사용됩니다.
3.4 useTransition (UI 업데이트 비동기 처리)
목적 | 비동기적 렌더링을 처리하여 UI 반응성을 유지 |
사용 시점 | 긴 렌더링 작업을 비동기적으로 처리하고 UI를 부드럽게 유지할 때 |
반환 값 | isPending (boolean)과 startTransition (function) |
주요 특성 | 긴 렌더링 작업을 비동기적으로 처리하여 UI 반응성을 유지. |
import React, { useState, useTransition } from 'react';
function Filter() {
const [filter, setFilter] = useState('');
const [isPending, startTransition] = useTransition(); // 트랜지션 시작
const data = Array.from({ length: 1000 }, (_, i) => `Item ${i + 1}`);
const filteredData = data.filter(item => item.includes(filter));
const handleFilterChange = (e) => {
startTransition(() => {
setFilter(e.target.value); // 비동기적으로 상태 업데이트
});
};
return (
<div>
<input
type="text"
value={filter}
onChange={handleFilterChange}
placeholder="필터 입력"
/>
{isPending ? <p>로딩 중...</p> : null}
<div>
{filteredData.map(item => <div key={item}>{item}</div>)}
</div>
</div>
);
}
export default Filter;
useTransition은 UI 업데이트를 비동기적으로 처리하여 렌더링 차단을 최소화합니다. 주로 긴 작업이나 대량 데이터 처리에서 UI의 반응성을 유지하기 위해 사용됩니다.
4. 고급 상태 관리 훅
4.1 useReducer (복잡한 상태 관리)
목적 | 복잡한 상태 로직을 관리하는 데 사용 |
사용 시점 | 상태 로직이 복잡할 때 (예: 여러 상태 변수, 액션 등) |
반환 값 | 현재 상태와 dispatch 함수 |
주요 특성 | 복잡한 상태 전환을 처리하는 데 유용. |
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>현재 카운트: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>증가</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>감소</button>
</div>
);
}
export default Counter;
4.2 useImperativeHandle (Ref 커스터마이징)
목적 | 부모 컴포넌트가 ref로 접근할 수 있는 값을 커스터마이징 |
사용 시점 | 부모 컴포넌트에 특정 메서드나 값을 노출할 때 |
반환 값 | 부모에게 노출할 메서드나 값을 담은 객체 |
주요 특성 | forwardRef와 함께 사용하여 부모가 접근할 값을 제어 |
import React, { useState, useRef, useImperativeHandle, forwardRef } from 'react';
const ChildComponent = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
// 부모 컴포넌트에 노출할 메서드 정의
useImperativeHandle(ref, () => ({
increment: () => setCount(count + 1),
decrement: () => setCount(count - 1),
}));
return <div>자식 컴포넌트 카운트: {count}</div>;
});
function ParentComponent() {
const childRef = useRef();
return (
<div>
<ChildComponent ref={childRef} />
<button onClick={() => childRef.current.increment()}>자식 증가</button>
<button onClick={() => childRef.current.decrement()}>자식 감소</button>
</div>
);
}
export default ParentComponent;
useImperativeHandle은 부모 컴포넌트가 자식 컴포넌트의 ref를 통해 호출할 수 있는 메서드를 정의합니다.
5.기타 (정리 필요)
useSyncExternalStore
목적 | 외부 상태를 구독하여 상태 업데이트를 처리 |
사용 시점 | 외부 스토어나 상태 관리 시스템에 구독할 때 |
반환 값 | 외부 스토어의 현재 상태 |
주요 특성 | 외부 상태 구독 시 사용. |
import React, { useSyncExternalStore } from 'react';
// 예시 외부 스토어 (간단한 구현)
let currentState = 0;
const listeners = new Set();
function subscribe(callback) {
listeners.add(callback);
return () => listeners.delete(callback);
}
function getState() {
return currentState;
}
function setState(value) {
currentState = value;
listeners.forEach(callback => callback());
}
function Counter() {
// useSyncExternalStore를 사용해 외부 스토어의 상태 구독
const state = useSyncExternalStore(subscribe, getState);
return (
<div>
<p>현재 카운트: {state}</p>
<button onClick={() => setState(state + 1)}>증가</button>
</div>
);
}
export default Counter;
외부 상태를 구독하고, React의 상태와 동기화합니다. React 컴포넌트에서 외부 상태(예: Redux, Zustand 등)나 외부 데이터 소스를 안전하게 구독할 수 있게 합니다.
useId
목적 | 컴포넌트 렌더링 간 유니크한 ID를 생성 |
사용 시점 | 접근성, 폼 입력 등에서 고유한 ID가 필요할 때 |
반환 값 | 유니크한 문자열 ID |
주요 특성 | 렌더링 간 고유한 ID 생성 (주로 접근성 관련). |
import React, { useId } from 'react';
function Form() {
const id = useId(); // Generate a unique ID
return (
<div>
<label htmlFor={`${id}-input`}>이메일:</label>
<input id={`${id}-input`} type="email" placeholder="이메일 입력" />
</div>
);
}
export default Form;
이렇게 하면 컴포넌트가 여러 번 렌더링되더라도 각 렌더링에서 고유한 ID가 유지되어 다른 요소와 충돌하지 않게 됩니다. useId는 주로 폼 요소나 접근성에 중요한 요소에서 유용하게 사용됩니다.
연습