지금까지 프론트엔드 개발을 하면서 여러번 구현한 기능이지만, 매번 짤때마다 까먹어서 다시 찾아보게되는 Drag & Drop
...
onDragOver
onDrop
이외에도 onDragEnter
, onDragLeave
... 등이 있지만 아래에 구현한 Drag & Drop
는 간소화에 포커스를 맞춰 최대한 간단하게 구현했으므로 필수적으로 필요한 위에 두개만 사용하였다. (여기서 onDrop
이 제대로 동작할려면 필수적으로 onDragOver
가 있어야한다.)
아래의 코드는 drag & drop
을 구현한 customHooks이다.
드롭할 영역의 ref와 드롭되었을때 실행할 callback function을 인자로 받는다.
/**
* @hooks
* File Input Drag & Drop customHooks
*/
export default function useDragAndDrop(
targetRef: RefObject<HTMLDivElement>,
callback: (file: File) => void,
) {
const handleDragOver = useCallback((e: DragEvent): void => {
e.preventDefault();
e.stopPropagation();
}, []);
const handleDrop = useCallback((e: DragEvent): void => {
e.preventDefault();
e.stopPropagation();
if (e.dataTransfer) {
const files = e.dataTransfer.files;
if (files && files.length > 0) {
callback(files[0]);
}
}
}, []);
useEffect(() => {
targetRef.current?.addEventListener('dragover', handleDragOver);
targetRef.current?.addEventListener('drop', handleDrop);
return () => {
targetRef.current?.removeEventListener('dragover', handleDragOver);
targetRef.current?.removeEventListener('drop', handleDrop);
};
}, []);
}
위에 작성한 customHooks를 사용해서 구현한 FileInput
컴포넌트이다.
drag & drop
은 드롭할 영역인 div와 실제 파일을 처리할 input (type='file') 이 필요하다.
input의 경우는 파일 처리만 수행해주면 되므로 display:'none'
처리를 해주었다.
export default function FileInput () {
const boxRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
// 영역 클릭 시 display:none 되어있는 file input open
const handleFileInputOpen = () => {
if (inputRef.current) {
inputRef.current.click();
}
};
// drag & drop customHooks
useDragAndDrop(componentRef, () => {
// 파일 업로드 logic callback function
});
return (
<div ref={boxRef} onClick={handleFileInputOpen}>
<input ref={inputRef} type="file" style={{display: 'none'}} />
</div>
)
}
정보 감사합니다.