useState 는 작아. 주변 함정은 아니야. 이 레슨이 함정 지도.
모양
const [value, setValue] = useState(initialValue). Hook 이 [현재 값, setter 함수] 튜플 반환. 현재 값은 매 render 마다 fresh. Setter 는 안정적 — render 간 identity 안 바뀜, dependency array 에 넣어도 재실행 안 트리거.
Lazy initializer
initialValue 계산 비싸면 함수 형태로: useState(() => computeIt()). React 가 초기 render 에 한 번 호출하고 영원히 무시. 평범한 호출 형태 useState(computeIt()) 는 React 가 첫 호출 결과만 써도 매 render 마다 계산 돔.
Functional update
새 값이 이전 값에 의존하면 setter 에 함수 넘겨: setCount(n => n + 1). Stale-closure 버그 (이벤트 핸들러가 옛 값 잡고 캡처 사이에 다른 업데이트 land) 막아. 새 값이 옛 값에 의존하면 항상 functional 형태 써.
State batching
React 19 가 같은 이벤트 핸들러, effect, async 함수 호출 안에서 일어나는 state 업데이트 다 배치. 클릭 핸들러의 setA(1); setB(2); 가 re-render 하나 트리거 — 둘 아님. 보통 신경 안 써도 됨 — 다만 업데이트 두 개가 render 하나 만들어서 놀라면 배칭이 이유.
Object 와 array — immutable update 만
State 가 reference equality 로 업데이트. 같은 객체 reference 로 setUser(user) 는 no-op. Object 필드 업데이트는 새 object 로 spread: setUser({ ...user, name: 'new' }). Array 도 마찬가지: setItems([...items, newItem]). 기존 object/array mutate 하고 setter 호출하면 re-render 안 트리거.
useState 여러 개 vs 큰 object 하나
두 state 값이 같이 바뀌면 묶어: useState({ x: 0, y: 0 }). 독립적으로 바뀌면 분리: useState(0); useState(0). 경험칙: 항상 같이 업데이트 되는 state 는 같이; 독립적으로 업데이트 되는 건 분리.
fullName 이 firstName + lastName 에서 계산되면 useState 에 저장 마. Render 에서 계산: const fullName = `${firstName} ${lastName}`. 파생 값 저장이 state-sync 버그의 시작.