리액트에서 엘리먼트를 꾹 눌렀을때에 대한 이벤트처리가 필요했다.
그래서 만든 customHook이다.
공통로직을 hook으로 뺴면서 event처리를 react외부에서 처리했다는 점에서 안티패턴인 것 같긴하다..
하지만 마땅히 좋은방법이 떠오르지 않았다.. ㅠㅠ
import { useState, useRef, useEffect, useLayoutEffect } from 'react';
const inEvents = ['touchstart', 'mousedown'];
const outEvents = ['touchcancel', 'click', 'touchend', 'mouseout'];
function useLongClick(targetEl: HTMLElement | null, delayMs: number | undefined = 400) {
const pressTimer = useRef<any>(null);
const [longClicked, setLongClicked] = useState<boolean>(false);
useLayoutEffect(() => {
if (targetEl) {
inEvents.forEach(eventNm => targetEl?.addEventListener(eventNm, touchStart));
outEvents.forEach(eventNm => targetEl?.addEventListener(eventNm, touchEnd));
}
return () => {
if (targetEl) {
inEvents.forEach(eventNm => targetEl?.removeEventListener(eventNm, touchStart));
outEvents.forEach(eventNm => targetEl?.removeEventListener(eventNm, touchEnd));
}
};
}, [targetEl]);
const touchStart = () => {
if (!pressTimer.current) {
pressTimer.current = setTimeout(() => {
if (pressTimer) {
setLongClicked(true);
}
}, delayMs);
}
};
const touchEnd = () => {
if (pressTimer.current) {
clearTimeout(pressTimer.current);
pressTimer.current = null;
setLongClicked(false);
}
};
return {
event: longClicked,
};
}
export default useLongClick;
function Button() {
const msgRef = useRef<any>(null);
const [msgEl, setMsgEl] = useState<HTMLElement | null>(msgRef.current);
const longClick = useLongClick(msgEl);
/*
msgRef.current를 useLongClick에 파라미터로 집어넣으면 msgRef의 초기값이 null로 넘어가게된다.
따라서 state를 넘기고 msgRef에 DOM이 세팅되는 시점에 state에 msgRef.current를 세팅해주는 방식으로 작업해야한다.
*/
useLayoutEffect(() => {
setMsgEl(msgRef.current);
}, []);
useEffect(() => {
console.log('fired longClick!!!');
}, [longClick.event])
return (
<Button>
<span ref={msgRef}>메세지</span>
</Button>
)
}