useClickOutside — ref callback 방식 정리
- ref callback은 DOM 노드 참조만 전달할 뿐, 내부에서 등록한 이벤트 리스너 등의 사이드이펙트는 자동 해제되지 않음
- React 18 이하: unmount 시
callback(null)호출되지만, 동일 함수 참조가 없어removeEventListener곤란 → 별도 ref 저장 필요 - React 19:
useEffect처럼 cleanup function return을 공식 지원 →return () => removeEventListener(...)패턴 사용 가능 - stale closure 방지는
handlerRef.current = handler패턴으로 해결 - 결론: React 19라면 ref callback + cleanup return이
useEffect방식의 깔끔한 대안
function useClickOutside(handler) {
const handlerRef = useRef(handler);
handlerRef.current = handler;
return useCallback((node) => {
const handleClick = (e) => {
if (!node.contains(e.target)) handlerRef.current();
};
document.addEventListener('mousedown', handleClick);
return () => document.removeEventListener('mousedown', handleClick);
}, []);
}
// 사용
function Modal({ onClose }) {
const ref = useClickOutside(onClose);
return <div ref={ref}>...</div>;
}