标题:如何用 120 行代码,实现交互完整的拖拽上传组件?
只看楼主
专注的蝴蝶
Rank: 1
等 级:新手上路
帖 子:12
专家分:0
注 册:2019-9-4
 问题点数:0 回复次数:0 
如何用 120 行代码,实现交互完整的拖拽上传组件?
逛国外社区时看到这篇:

How To Implement Drag and Drop for Files in React
文章讲了React拖拽上传的精简实现,但直接翻译照搬显然不是我的风格。
于是我又用React Hooks 重写了一版,除CSS的代码总数 120行。
app.js

import React from 'react';
import PropTypes from 'prop-types';

import { FilesDragAndDrop } from '../components/Common/FilesDragAndDropHook';

export default class App extends {
    static propTypes = {};

    onUpload = (files) => {
        console.log(files);
    };

    render() {
        return (
            <div>
                <FilesDragAndDrop
                    onUpload={this.onUpload}
                />
            </div>
        );
    }
}
FilesDragAndDrop.js(非Hooks):

import React from 'react';
import PropTypes from 'prop-types';

import '../../scss/components/Common/FilesDragAndDrop.scss';

export default class FilesDragAndDrop extends {
    static propTypes = {
        onUpload: PropTypes.func.isRequired,
    };

    render() {
        return (
            <div className='FilesDragAndDrop__area'>
                传下文件试试?
                <span
                    role='img'
                    aria-label='emoji'
                    className='area__icon'
                >
                    😎
                </span>
            </div>
        );
    }
}
import React, { useEffect, useState, useRef } from "react";
import PropTypes from 'prop-types';
import classNames from 'classnames';
import classList from '../../scss/components/Common/FilesDragAndDrop.scss';
const FilesDragAndDrop = (props) => {
    return (
        <div className='FilesDragAndDrop__area'>
            传下文件试试?
            <span
                role='img'
                aria-label='emoji'
                className='area__icon'
            >
                😎
            </span>
        </div>
    );
}

FilesDragAndDrop.propTypes = {
    onUpload: PropTypes.func.isRequired,
    children: PropTypes.node.isRequired,
    count: PropTypes.number,
    formats: PropTypes.arrayOf(PropTypes.string)
}

export { FilesDragAndDrop };

FilesDragAndDrop.scss

.FilesDragAndDrop {
  .FilesDragAndDrop__area {
    width: 300px;
    height: 200px;
    padding: 50px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-flow: column nowrap;
    font-size: 24px;
    color: #555555;
    border: 2px #c3c3c3 dashed;
    border-radius: 12px;

    .area__icon {
      font-size: 64px;
      margin-top: 20px;
    }
  }
}
import React, { useEffect, useState, useRef } from "react";
import PropTypes from 'prop-types';
import classNames from 'classnames';
import classList from '../../scss/components/Common/FilesDragAndDrop.scss';

const FilesDragAndDrop = (props) => {
    const [dragging, setDragging] = useState(false);
    const [message, setMessage] = useState({ show: false, text: null, type: null });
    const drop = useRef();
    const drag = useRef();
    useEffect(() => {
        // useRef 的 drop.current 取代了外汇返佣www.kaifx,cn ref 的 this.drop
        drop.current.addEventListener('dragover', handleDragOver);
        drop.current.addEventListener('drop', handleDrop);
        drop.current.addEventListener('dragenter', handleDragEnter);
        drop.current.addEventListener('dragleave', handleDragLeave);
        return () => {
            drop.current.removeEventListener('dragover', handleDragOver);
            drop.current.removeEventListener('drop', handleDrop);
            drop.current.removeEventListener('dragenter', handleDragEnter);
            drop.current.removeEventListener('dragleave', handleDragLeave);
        }
    })
    const handleDragOver = (e) => {
        e.preventDefault();
        e.stopPropagation();
    };

    const handleDrop = (e) => {
        e.preventDefault();
        e.stopPropagation();
        setDragging(false)
        const { count, formats } = props;
        const files = [...e.dataTransfer.files];

        if (count && count < files.length) {
            showMessage(`抱歉,每次最多只能上传${count} 文件。`, 'error', 2000);
            return;
        }

        if (formats && files.some((file) => !formats.some((format) => file.name.toLowerCase().endsWith(format.toLowerCase())))) {
            showMessage(`只允许上传 ${formats.join(', ')}格式的文件`, 'error', 2000);
            return;
        }

        if (files && files.length) {
            showMessage('成功上传!', 'success', 1000);
            props.onUpload(files);
        }
    };

    const handleDragEnter = (e) => {
        e.preventDefault();
        e.stopPropagation();
        e.target !== drag.current && setDragging(true)
    };

    const handleDragLeave = (e) => {
        e.preventDefault();
        e.stopPropagation();
        e.target === drag.current && setDragging(false)
    };

    const showMessage = (text, type, timeout) => {
        setMessage({ show: true, text, type, })
        setTimeout(() =>
            setMessage({ show: false, text: null, type: null, },), timeout);
    };

    return (
        <div
            ref={drop}
            className={classList['FilesDragAndDrop']}
        >
            {message.show && (
                <div
                    className={classNames(
                        classList['FilesDragAndDrop__placeholder'],
                        classList[`FilesDragAndDrop__placeholder--${message.type}`],
                    )}
                >
                    {message.text}
                    <span
                        role='img'
                        aria-label='emoji'
                        className={classList['area__icon']}
                    >
                        {message.type === 'error' ? <>😢</> : <>😘</>}
                    </span>
                </div>
            )}
            {dragging && (
                <div
                    ref={drag}
                    className={classList['FilesDragAndDrop__placeholder']}
                >
                    请放手
                    <span
                        role='img'
                        aria-label='emoji'
                        className={classList['area__icon']}
                    >
                        😝
                    </span>
                </div>
            )}
            {props.children}
        </div>
    );
}

FilesDragAndDrop.propTypes = {
    onUpload: PropTypes.func.isRequired,
    children: PropTypes.node.isRequired,
    count: PropTypes.number,
    formats: PropTypes.arrayOf(PropTypes.string)
}

export { FilesDragAndDrop };
import React, { Component } from 'react';
import { FilesDragAndDrop } from '../components/Common/FilesDragAndDropHook';
import classList from '../scss/components/Common/FilesDragAndDrop.scss';

export default class App extends Component {
    onUpload = (files) => {
        console.log(files);
    };
    render () {
        return (
            <FilesDragAndDrop
                onUpload={this.onUpload}
                count={1}
                formats={['jpg', 'png', 'gif']}
            >
                <div className={classList['FilesDragAndDrop__area']}>
                    传下文件试试?
            <span
                        role='img'
                        aria-label='emoji'
                        className={classList['area__icon']}
                    >
                        😎
            </span>
                </div>
            </FilesDragAndDrop>
        )
    }
}
FilesDragAndDrop.scss:
.FilesDragAndDrop {
  position: relative;

  .FilesDragAndDrop__placeholder {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: 100%;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-flow: column nowrap;
    background-color: #e7e7e7;
    border-radius: 12px;
    color: #7f8e99;
    font-size: 24px;
    opacity: 1;
    text-align: center;
    line-height: 1.4;

    &.FilesDragAndDrop__placeholder--error {
      background-color: #f7e7e7;
      color: #cf8e99;
    }

    &.FilesDragAndDrop__placeholder--success {
      background-color: #e7f7e7;
      color: #8ecf99;
    }

    .area__icon {
      font-size: 64px;
      margin-top: 20px;
    }
  }
}

.FilesDragAndDrop__area {
  width: 300px;
  height: 200px;
  padding: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-flow: column nowrap;
  font-size: 24px;
  color: #555555;
  border: 2px #c3c3c3 dashed;
  border-radius: 12px;

  .area__icon {
    font-size: 64px;
    margin-top: 20px;
  }
 }
搜索更多相关主题的帖子: const from import div Drop 
2019-09-04 16:34



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-496685-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.298491 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved