useState는 리액트에서 정말 자주 사용되는 리액트 훅입니다.
의도한 대로 동작하지 않는 느낌이 들어서 어떤 방식으로 작동하는지 확인하고 싶었습니다.
useState에 대한 제 첫 의문은 이것이었습니다.
const [checked, setChecked] = useState(false);
const onClickHandler = () => {
setChecked(!checked);
console.log(checked);
};
return (
<div>
<input type="checkbox" onClick={onClickHandler} />
<label>Checkbox</label>
</div>
);
set으로 체크박스의 상태를 바꿔준 뒤 state로 상태를 가져오고자 했습니다.

결과는 위의 이미지처럼 체크박스의 value가 정반대의 결과로 찍힙니다.
state는 즉각적으로 반영되는 것처럼 보이지는 않지만, 클릭했을 때마다 변경이 이루어지고 있습니다.
요즘에는 사용을 해본 오픈소스가 어떤 식으로 작성되어 있는지 구경하고 읽는 재미가 붙었기 때문에 useState도 같은 방식으로 찾아보고자 했습니다.
node_modules에 있는 react 폴더를 뒤졌는데 외부에선 확인할 수 없게 숨겨져 있어서 리액트 훅의 원본 소스를 확인할 수가 없었습니다.
리액트 공식문서는 state가 변경되지 않는 이유를 스냅샷처럼 동작하기 때문이라고 설명했습니다.
state를 업데이트(set)하면 리액트에 리렌더링을 요청하지만, 이미 실행 중인 이벤트 핸들러의 변수에는 영향을 미치지 않습니다.
이번에 공부를 하며 리액트 훅은 모두 클로저(closure)라는 말에 머리를 한 대 맞은 기분이 들었는데요.
클로저는 내부함수에서 상위 함수 스코프의 변수에 접근하는 개념입니다.
setState는 state를 직접 변경하는 것이 아니라, useState의 상위에 선언된 변수를 변경할 뿐이라는 겁니다!
다시 체크박스의 이벤트 핸들러로 돌아와보겠습니다.
const onClickHandler = () => {
setChecked(!checked); //!checked는 nextState
console.log(!checked);
};
checked의 다음 값을 가져와주었습니다.
공식 문서처럼 변경할 값을 변수에 미리 저장해서 가져올 수도 있습니다.
const onClickHandler = () => {
const nextState = !checked;
setChecked(nextState);
console.log(nextState);
};

참고자료
https://react-ko.dev/reference/react/useState
https://react-ko.dev/learn/state-as-a-snapshot