연결한 ref 외의 것을 클릭하면 이벤트가 발생하는 훅!
훅 만들기
- 매개변수 : 핸들러
- 리턴 값 : ref
만드는 순서
- 이벤트 이름 배열 생성
- 'mousedown' (cf. 클릭 이벤트는 mousedown > mouseup, click 순으로 - 참고)
- 'touchStart' : for 모바일
- useRef로 ref 생성
- useEffect 안에서 아래 로직 수행
- dependency로 ref와 handler를
- ref가 없다면 early return
- 이벤트 함수 ⇒ e.target이 ref에 속해있지 않다면 핸들러 실행
- 이벤트 등록 ⇒ 1을 for문으로 돌아서 document에 이벤트 b를 등록
- 이벤트 해제 ⇒ return 값으로 1을 for문으로 돌아서 document에 이벤트 b를 지우는 함수를
import { useEffect, useRef } from 'react'; const eventName = ['mousedown', 'touchStart']; const useClickAway = (handler) => { const ref = useRef(null); useEffect(() => { const element = ref.current; if (!element) { return; } const touchEvent = (e) => { !element.contains(e.target) && handler(e); }; for (const event of eventName) { document.addEventListener(event, touchEvent); } return () => { for (const event of eventName) { document.removeEventListener(event, touchEvent); } }; }, [handler, ref]); return ref; }; export default useClickAway;
소소한 성능 개선
handler를 ref 상수에 담아서 event 등록 useEffect 디펜던시에서 뺌
... const useClickAway = (handler) => { const ref = useRef(null); const savedHandler = useRef(handler); useEffect(()=>{ savedHandler.current = handler },[handler]) useEffect(()=>{ ... const touchEvent = (e) => { !element.contains(e.target) && savedHandler(e); }; },[ref])
사용하기
- 쓰는 곳에서 이벤트를 매개변수로 가지는 함수를 useClickAway의 매개변수로 넣어서 실행하면 됨
const [show, setShow] = useState(false) const ref = useClickAway((e)=>{ setShow(true) }) ... <div ref={ref} .. />