React 19.2 useEffectEvent
2025년 10월 1일 React 19.2가 릴리즈됐다. 이 버전에서 새로운 훅이 하나 추가되었는데 useEffectEvent라는 훅이다.
오래된 클로저
export default function WatchCount() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
console.log(`Count is: ${count}`);
}, 2000);
return () => clearInterval(interval);
}, []);
return (
<div>
{count}
<button onClick={() => setCount(count + 1)}>Increase</button>
</div>
);
}오래된 클로저를 설명하는 대표적인 예시 코드다. useEffect 콜백에서 외부 상태 count를 참조하는데 버튼을 아무리 클릭해도 로그에는 0이 출력된다. 클로저는 생성 당시 외부 참조 변수를 기억하고 있는데 의존성 배열이 빈 배열이므로 콜백 함수는 단 한 번 생성된다. 그렇기에 항상 0을 기억할 수밖에 없다.
useEffect의 의존성 배열에 count를 넣어주어야 오래된 클로저 이슈를 해결할 수 있다.
다른 예제
export default function Component() {
const [userName, setUserName] = useState('홍길동');
useEffect(() => {
let timer = 0;
const interval = setInterval(() => {
console.log(`${userName} 로그인 경과 시간: ${++timer}초`);
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<div>
<input
type="text"
value={userName}
onChange={(e) => setUserName(e.target.value)}
/>
</div>
);
}userName을 변경해도 로그에는 처음 지정된 "홍길동"만 출력된다. 이 경우에는 의존성 배열을 추가하면 로그인 경과 시간이 초기화되므로 useRef를 사용한다.
const usernameRef = useRef(userName);
usernameRef.current = userName;
useEffect(() => {
let timer = 0;
const interval = setInterval(() => {
console.log(`${usernameRef.current} 로그인 경과 시간: ${++timer}초`);
}, 1000);
return () => clearInterval(interval);
}, []);useEffectEvent
export default function Component() {
const [userName, setUserName] = useState("홍길동");
const getName = useEffectEvent(() => userName);
useEffect(() => {
let timer = 0;
const interval = setInterval(() => {
console.log(`${getName()} 로그인 경과 시간: ${++timer}초`);
}, 1000);
return () => clearInterval(interval);
}, []);useEffectEvent를 사용해 로직을 분리했다. 분리된 함수는 실행될 때마다 항상 최신의 props와 state 값을 참조한다. 이를 통해 오래된 클로저 문제를 해결할 수 있다.
이처럼 useEffectEvent를 사용하면 의존성 배열에서 상태에 대한 의존성을 제거할 수 있으며 보다 안전한 Effect를 사용할 수 있다.
주의 사항
useEffectEvent는 컴포넌트 최상위 레벨에서 호출해야 하며, Effect 내부에서 사용되어야 한다.