两个 hook 搞定拖拽和粘贴文件

21 阅读1分钟

前言

做的需求需要可以在页面上拖拽和粘贴图片,为了在多个地方复用就封装成 hook 来使用。实现思路都是通过外部调用时主动传入 ref 在上面绑定监听事件。废话不多说,直接上代码吧。

拖拽文件

import React, { useCallback, useEffect, useState } from 'react';

export interface ImageDropInProps {
  ref: React.RefObject<HTMLDivElement>;
  onFinished?: (files: File[]) => Promise<void> | void;
}

export default function useDragInImage(props: ImageDropInProps) {
  const { ref, onFinished } = props;
  const [isDrop, setIsDragOver] = useState(false);

  const onDrop = useCallback(
    async (event: React.DragEvent<HTMLDivElement>) => {
      event.preventDefault();
      setIsDragOver(false);

      const { files } = event.dataTransfer;
      if (!files) {
        return;
      }

      const selectedFiles = Array.from(files);
      onFinished?.(selectedFiles);
    },
    [onFinished]
  );

  const onDragOver = useCallback((event: React.DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    setIsDragOver(true);
  }, []);

  const onDragLeave = useCallback((event: React.DragEvent<HTMLDivElement>) => {
    event.preventDefault();
    setIsDragOver(false);
  }, []);

  useEffect(() => {
    if (!ref.current) {
      return;
    }
    const dragInImage = ref.current;
    const onDropListener = (event: DragEvent) => {
      onDrop(event as unknown as React.DragEvent<HTMLDivElement>);
    };
    const onDragOverListener = (event: DragEvent) => {
      onDragOver(event as unknown as React.DragEvent<HTMLDivElement>);
    };
    const onDragLeaveListener = (event: DragEvent) => {
      onDragLeave(event as unknown as React.DragEvent<HTMLDivElement>);
    };
    dragInImage.addEventListener('drop', onDropListener);
    dragInImage.addEventListener('dragover', onDragOverListener);
    dragInImage.addEventListener('dragleave', onDragLeaveListener);
    return () => {
      dragInImage.removeEventListener('drop', onDropListener);
      dragInImage.removeEventListener('dragover', onDragOverListener);
      dragInImage.removeEventListener('dragleave', onDragLeaveListener);
    };
  }, [onDragLeave, onDragOver, onDrop, ref]);

  return { isDrop };
}

复制文件

import React, { useCallback, useEffect } from 'react';

export interface ParseImageProps {
  ref: React.RefObject<HTMLDivElement>;
  onFinished?: (files: File[]) => Promise<void> | void;
}

export default function useParseImage(props: ParseImageProps) {
  const { ref, onFinished } = props;

  const onParse = useCallback(
    async (event: React.ClipboardEvent<HTMLDivElement>) => {
      const { clipboardData } = event;
      if (!clipboardData) {
        return;
      }
      const { items } = clipboardData;
      if (!items) {
        return;
      }
      const files = Array.from(items).map(item => item.getAsFile()) as File[];
      if (!files) {
        return;
      }
      onFinished?.(files);
    },
    [onFinished]
  );

  useEffect(() => {
    if (!ref.current) {
      return;
    }
    const { current } = ref;
    const onParseListener = (event: ClipboardEvent) => {
      onParse(event as unknown as React.ClipboardEvent<HTMLDivElement>);
    };

    current.addEventListener('paste', onParseListener);
    return () => {
      current.removeEventListener('paste', onParseListener);
    };
  }, [onParse, ref]);
}