предисловие
В этой статье вы узнаете:
- Как переписать существующий компонент в
React Hooks
функциональный компонент -
useState
,useEffect
,useRef
как заменить исходный жизненный цикл иRef
из. - Полное поведение загрузки с помощью перетаскивания охватывает четыре события:
dragover
,dragenter
,drop
,dragleave
- как использовать
React Hooks
Напишите свою собственную библиотеку компонентов пользовательского интерфейса.
При посещении иностранных сообществ я увидел это:
в статье сказаноReact
Упрощенная реализация загрузки с помощью перетаскивания, но прямой перевод явно не в моем стиле.
Поэтому я используюReact Hooks
Переписанная версия, кромеCSS
общее количество кодов120
Ряд.
Эффект следующий:
1. Добавьте скелет базового каталога
app.js
import React from 'react';
import PropTypes from 'prop-types';
import { FilesDragAndDrop } from '../components/Common/FilesDragAndDropHook';
export default class App extends React.Component {
static propTypes = {};
onUpload = (files) => {
console.log(files);
};
render() {
return (
<div>
<FilesDragAndDrop
onUpload={this.onUpload}
/>
</div>
);
}
}
FilesDragAndDrop.js (не хуки):
import React from 'react';
import PropTypes from 'prop-types';
import '../../scss/components/Common/FilesDragAndDrop.scss';
export default class FilesDragAndDrop extends React.Component {
static propTypes = {
onUpload: PropTypes.func.isRequired,
};
render() {
return (
<div className='FilesDragAndDrop__area'>
传下文件试试?
<span
role='img'
aria-label='emoji'
className='area__icon'
>
😎
</span>
</div>
);
}
}
1. Как переписать какHooks
компоненты?
Пожалуйста, посмотрите анимацию:
2. Перепишите компонент
Hooks
Компонент версии относится к функциональному компоненту, который будет дооснащен:
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;
}
}
}
Затем вы можете увидеть страницу:
2. Анализ реализации
От манипулирования DOM, повторного использования компонентов, запуска событий, предотвращения поведения по умолчанию иHooks
Анализ приложений.
1. Управление DOM:useRef
Поскольку вам нужно перетаскивать файлы для загрузки и управления экземплярами компонентов, вам нужно использоватьref
Атрибуты.
React Hooks
в добавленномuseRef API
грамматика
const refContainer = useRef(initialValue);
-
useRef
вернуть изменяемыйref
объект, . - Это.currentСвойства инициализируются переданными аргументами (
initialValue
) - Возвращенный объект будет сохраняться в течение всего времени существования компонента.
...
const drop = useRef();
return (
<div
ref={drop}
className='FilesDragAndDrop'
/>
...
)
2. Триггер события
Завершить поведение перетаскивания с динамическим взаимодействием непросто, и требуются четыре элемента управления событиями:
- За пределами области:
dragleave
, вне диапазона - в районе:
dragenter
, который используется для определения того, принимает ли цель перетаскивания перетаскивание. - Движение по территории:
dragover
, который используется для определения того, какую обратную связь отображать пользователю. - Чтобы завершить перетаскивание (падение):
drop
, позволяя размещать объекты.
Сосуществование этих четырех событий необходимо для предотвращения поведения веб-браузера по умолчанию и формирования обратной связи.
3. Блокировать поведение по умолчанию
Код прост:
e.preventDefault() //阻止事件的默认行为(如在浏览器打开文件)
e.stopPropagation() // 阻止事件冒泡
Каждая фаза события должна быть заблокирована, почему? Возьмите 🌰 каштан:
const handleDragOver = (e) => {
// e.preventDefault();
// e.stopPropagation();
};
Если он не заблокирован, он вызовет поведение открытия файла, что, очевидно, не то, что мы хотим видеть.
4. Внутреннее состояние компонента:useState
Компонент загрузки с помощью перетаскивания, в дополнение к базовому элементу управления состоянием перетаскивания, также должен иметь напоминание о сообщении, когда файл успешно загружен или не прошел проверку. Государственный состав должен быть:
state = {
dragging: false,
message: {
show: false,
text: null,
type: null,
},
};
написать соответствующийuseState
Прежде чем вернуться к следующему письму:
const [属性, 操作属性的方法] = useState(默认值);
Так становится:
const [dragging, setDragging] = useState(false);
const [message, setMessage] = useState({ show: false, text: null, type: null });
5. Требуется вторая накладка
Кромеdrop
событие, остальные три события изменяются динамически, а при перетаскивании элемента все остальные350
миллисекунды сработаютdragover
мероприятие.
В это время секундуref
для единого контроля.
Таким образом, полный `ref`:
const drop = useRef(); // 落下层
const drag = useRef(); // 拖拽活动层
6. Тип файла и контроль количества
Когда мы применяем компонент,prop
Необходимо передать тип и количество для контроля
<FilesDragAndDrop
onUpload={this.onUpload}
count={1}
formats={['jpg', 'png']}
>
<div className={classList['FilesDragAndDrop__area']}>
传下文件试试?
<span
role='img'
aria-label='emoji'
className={classList['area__icon']}
>
😎
</span>
</div>
</FilesDragAndDrop>
-
onUpload
: Перетащите событие завершения обработки -
count
: контроль количества -
formats
: тип файла.
Соответствующий компонентDrop
Внутренние события:handleDrop
:
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);
}
};
.endsWith
заключается в оценке конца строки, например:"abcd".endsWith("cd"); // true
showMessage
Это управление отображаемым текстом:
const showMessage = (text, type, timeout) => {
setMessage({ show: true, text, type, })
setTimeout(() =>
setMessage({ show: false, text: null, type: null, },), timeout);
};
Необходимо запустить таймер, чтобы вернуться в исходное состояние
7. Инициирование и уничтожение событий в жизненном цикле
оригинальныйEventListener
Мероприятие должно быть вcomponentDidMount
добавитьcomponentWillUnmount
Разрушен в:
componentDidMount () {
this.drop.addEventListener('dragover', this.handleDragOver);
}
componentWillUnmount () {
this.drop.removeEventListener('dragover', this.handleDragOver);
}
ноHooks
Существуют внутренние методы работы и соответствующиеuseEffect
заменить два вышеуказанных жизненных цикла
useEffect
Пример:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
а такжекаждыйeffect
Оба могут возвращать функцию очистки. Это добавит(componentDidMount
) и удалить (componentWillUnmount
) собрана логика подписки.
Таким образом, вышеизложенное можно записать как:
useEffect(() => {
drop.current.addEventListener('dragover', handleDragOver);
return () => {
drop.current.removeEventListener('dragover', handleDragOver);
}
})
Это слишком вкусно! ! !
3. Полный код:
FilesDragAndDropHook.js
:
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 取代了 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 };
App.js
:
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;
}
}
Затем вы можете получить файл и играть медленно. . .
❤️ После прочтения трех вещей
Если вы найдете этот контент вдохновляющим, я хотел бы пригласить вас сделать мне три небольших одолжения:
- Ставьте лайк, чтобы больше людей увидело этот контент
- Обратите внимание на паблик «Учитель фронтенд-убеждения», и время от времени делитесь оригинальными знаниями.
- Также смотрите другие статьи
- Те шаблоны проектирования, которые вы используете непреднамеренно (1) - шаблоны создания
- [«Король библиотеки визуализации данных» D3.js быстро приступает к работе с приложениями Vue
](nuggets.capable/post/684490…)
- Руководство "True® Full Stack Road" для веб-интерфейсной разработки
- «Практика Vue» — плагин Vue CLI за 5 минут
- Вооружите свой интерфейсный проект «Практикой Vue»
- «Intermediate and Advanced Front-End Interview» JavaScript Рукописный код Invincible Cheats
- «Узнайте из исходного кода» ответы на вопросы Vue, которые интервьюеры не знают
- JS-операция «Узнать из исходного кода» в исходном коде Vue
- Правильная позиция для обновления vue-cli3 в проекте "Vue Practice"
- Почему вы до сих пор не можете понять цепочку областей видимости JavaScript?