Приложения с перетаскиванием очень распространены в наши дни, они отлично подходят для пользовательского опыта внутри приложения. **И вы наверняка хотели бы реализовать их в своем следующем проекте.
На этот раз я покажу вам, как сделать приложение с функцией drag&drop, но без использования каких-либо внешних библиотек, только с помощью React JS.
🚨 Примечание: Этот пост требует от вас знания основ React с TypeScript (базовые хуки и пользовательские хуки).
Любой вид обратной связи приветствуется, спасибо и надеюсь, что статья вам понравится.🤗
- Оглавление.
- 👉 Используемые технологии.
- 👉 Создание проекта.
- 👉 Первые шаги.
- 👉 Создаем наши карточки.
- 👉 Создание контейнеров для наших карточек.
- 🟠 Определение типа и интерфейса для информации о карточках.
- 👉 Создание компонента DragAndDrop.tsx.
- 👉 Добавление некоторых данных для создания карточек.
- 👉 Показ некоторых карточек.
- 👉 Выполнение перетаскивания.
- 👉 Выполнение сброса.
- 🟠 Создание состояния для хранения карт.
- 👉 Выполняем функции, чтобы сделать падение в контейнерах.
- 👉 Дополнительно. Рефакторинг кода в DragAndDrop.tsx
- 👉 Заключение.
- 🟠 Live demo.
- 🟠 Исходный код.
- Franklin361 / drag-and-drop-react
- Создание приложения с помощью Drag & Drop с React JS 🤏
Оглавление.
📌 Используемые технологии.
📌 Создание проекта.
📌 Первые шаги.
📌 Создание наших карточек.
📌 Создание контейнеров для наших карточек.
📌 Определение типа и интерфейса для информации о карточках.
📌 Создание компонента DragAndDrop.tsx.
📌 Добавление некоторых данных для создания карточек.
📌 Показ некоторых карточек.
📌 Выполнение перетаскивания.
📌 Выполнение операции Drop.
📌 Создание состояния для хранения карточек.
📌 Выполнение функций, чтобы сделать сброс в контейнеры.
📌 Дополнительно. Рефакторинг кода в
DragAndDrop.tsx
.📌 Заключение.
📌 Живая демонстрация.
📌 Исходный код.
👉 Используемые технологии.
- ▶️ React JS (v.18)
- ▶️ Vite JS
- ▶️ TypeScript
- ▶️ CSS vanilla (стили можно найти в репозитории в конце этого поста)
👉 Создание проекта.
Мы назовем проект: dnd-app
(опционально, вы можете назвать его как угодно).
npm init vite@latest
Мы создаем проект с помощью Vite JS и выбираем React with TypeScript.
Затем мы выполняем следующую команду для перехода в только что созданную директорию.
cd dnd-app
Затем мы устанавливаем зависимости.
npm install
Затем открываем проект в редакторе кода (в моем случае VS code).
code .
Затем с помощью этой команды мы поднимем сервер разработки и, наконец, зайдем в браузер и откроем http://localhost:5173
(в vite версии 2 порт был localhost:3000
, а в новой версии порт localhost:5173
).
npm run dev
👉 Первые шаги.
Сразу создаем папку src/components
и добавляем в нее файл Title.tsx
и внутри добавляем:
export const Title = () => {
return (
<div className="title flex">
<h1>Creating basic Drag & Drop 👆 </h1>
<span>( without external libraries )</span>
</div>
)
}
Теперь, внутри файла src/App.tsx
мы удаляем все содержимое файла и размещаем функциональный компонент, который показывает заголовок, который мы только что создали.
import { Title } from "./components/Title"
const App = () => {
return (
<div className="container-main flex">
<Title />
</div>
)
}
export default App
Это должно выглядеть следующим образом 👀:
👉 Создаем наши карточки.
В папку src/components
добавляем файл CardItem.tsx
.
В данный момент он не будет получать никаких реквизитов, он сделает это позже.
export const CardItem = () => {
return (
<div className='card-container'>
<p>content</p>
</div>
)
}
Мы пока НЕ будем использовать компонент Card в файле, но если вы хотите, вы можете импортировать его в файл src/App.tsx
, чтобы вы могли стилизовать его и увидеть на экране.
👉 Создание контейнеров для наших карточек.
Теперь давайте создадим наш контейнер для карточек.
Внутри папки src/components
добавим файл ContainerCards.tsx
и добавим следующее:
Этот компонент на данный момент получает в качестве параметра статус (вы можете видеть, какой тип является статусом).
import { Status } from '../interfaces'
interface Props {
status: Status
}
export const ContainerCards = ({ status }: Props) => {
return (
<div className="layout-cards" >
<p>{status} hero</p>
{/* Cards */}
</div>
)
}
🟠 Определение типа и интерфейса для информации о карточках.
Тип Статус выглядит следующим образом:
export type Status = 'good' | 'bad' | 'normal'
Этот тип находится в папке src/interfaces
внутри файла index.ts
(который необходимо создать, так как тип Status будет использоваться в нескольких файлах).
При создании index.ts
в src/interfaces
также добавьте следующий интерфейс.
Вот как будут выглядеть данные карточки.
export interface Data {
id: number
content: string
status: Status
}
👉 Создание компонента DragAndDrop.tsx.
Итак, на данный момент мы уже создали компонент, который будет содержать карты, но нам нужно 3 контейнера для карт:
- Один для хороших героев.
- Один для нормальных героев.
- Один для плохих героев.
В папку src/components
добавляем файл DragAndDrop.tsx
и добавляем следующее:
import { Status } from "../interfaces"
import { ContainerCards } from "./ContainerCards"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
return (
<div className="grid">
{
typesHero.map( container => (
<ContainerCards
status={container}
key={container}
/>
))
}
</div>
)
}
Этот компонент должен быть добавлен в src/App.tsx
.
import { DragAndDrop} from "./components/DragAndDrop"
import { Title } from "./components/Title"
const App = () => {
return (
<div className="container-main flex">
<Title />
<DragAndDrop />
</div>
)
}
export default App
На данный момент это должно выглядеть примерно так 👀….
Теперь у нас есть контейнеры, в которые можно бросать и сортировать карты. 👋
Теперь нам нужно создать несколько карточек.
👉 Добавление некоторых данных для создания карточек.
Теперь создадим папку src/assets
и внутри нее файл index.ts
, который будет содержать список с данными для заполнения карточек.
import { Data } from "../interfaces";
export const data: Data[] = [
{
id: 1,
content: 'Aqua-man',
status: 'good'
},
{
id: 2,
content: 'Flash',
status: 'normal'
},
{
id: 3,
content: 'Green Lantern',
status: 'good'
},
{
id: 4,
content: 'Batman',
status: 'bad'
},
]
Теперь, вернувшись в src/components/DragAndDrop.tsx
в компонент ContainerCards, передадим новый prop под названием items, которому в качестве значения передадим данные, созданные нами в папке src/assets
.
import { ContainerCards } from "./ContainerCards"
import { Status } from "../interfaces"
import { data } from "../assets"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
return (
<div className="grid">
{
typesHero.map( container => (
<ContainerCards
status={container}
key={container}
items={data}
/>
))
}
</div>
)
}
Это приведет к ошибке, потому что items не является свойством, которое ожидает ContainerCards. 😥
Но мы исправим это в следующем разделе. 👇
👉 Показ некоторых карточек.
Чтобы отобразить некоторые карточки, нам нужно внести некоторые изменения в параметры каждого компонента.
1 — Сначала компонент src/components/CardItem.tsx
.
Он будет получать в качестве реквизитов данные типа Data, которые мы определили ранее.
Сразу же покажем содержимое свойства внутри данных.
import { Data } from "../interfaces"
interface Props {
data: Data
}
export const CardItem = ({ data, handleDragging }: Props) => {
return (
<div className='card-container'>
<p>{data.content}</p>
</div>
)
}
2 — В компоненте src/components/ContainerCards.tsx
мы изменим интерфейс реквизита, добавив свойство items, которое является списком Data, и деструктурируем его в компоненте.
import { Data, Status } from "../interfaces"
interface Props {
items: Data[]
status: Status
}
export const ContainerCards = ({ items = [], status }: Props) => {
return (
<div className="layout-cards">
<p>{status} hero</p>
</div>
)
}
Затем в теге p
мы выполняем итерацию по элементам.
И возвращаем CardItem.tsx
, отправляя item
в свойство data
CardItem.
import { Data, Status } from "../interfaces"
import { CardItem } from "./CardItem"
interface Props {
items: Data[]
status: Status
}
export const ContainerCards = ({ items = [], status}: Props) => {
return (
<div className="layout-cards">
<p>{status} hero</p>
{
items.map(item => (
<CardItem
data={item}
key={item.id}
/>
))
}
</div>
)
}
При этом появится предупреждение о том, что клавиши повторяются 😥.
Это потому, что мы рендерим 3 раза ContainerCards.
Но подождите, единственное свойство, которое будет делать разницу между этими 3 компонентами — это статус.
Поэтому мы сделаем следующее условие:
- Если статус, полученный компонентом ContainerCards, равен статусу элемента (т.е. супергероя), то отрисовываем его, иначе возвращаем false.
import { Data, Status } from "../interfaces"
import { CardItem } from "./CardItem"
interface Props {
items: Data[]
status: Status
}
export const ContainerCards = ({ items = [], status }: Props) => {
return (
<div className="layout-cards">
<p>{status} hero</p>
{
items.map(item => (
status === item.status
&& <CardItem
data={item}
key={item.id}
/>
))
}
</div>
)
}
Таким образом, мы избежим конфликта с ключами и карточки будут отсортированы следующим образом 👀….
👉 Выполнение перетаскивания.
Чтобы выполнить функцию перетаскивания, мы сначала определим состояние и функцию в src/components/DragAndDrop.tsx
.
-
Состояние поможет нам узнать, выполняется ли перетаскивание, и таким образом изменить стили.
- По умолчанию оно будет равно false, так как в начале работы приложения оно не будет выполнять перетаскивание.
- Оно будет истинным только при перетаскивании карты.
-
Функция, которая получает булево значение, поможет нам изменить значение на состояние, это делается для того, чтобы избежать передачи сеттера setter setIsDragging как prop.
Мы передаем в качестве prop компоненту ContainerCards:
- isDragging, он будет иметь значение состояния.
- handleDragging, это будет функция, которую мы создадим для обновления состояния.
import { ContainerCards } from "./ContainerCards"
import { data } from "../assets"
import { Status } from "../interfaces"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
const [isDragging, setIsDragging] = useState(false)
const handleDragging = (dragging: boolean) => setIsDragging(dragging)
return (
<div className="grid">
{
typesHero.map(container => (
<ContainerCards
items={data}
status={container}
key={container}
isDragging={isDragging}
handleDragging={handleDragging}
/>
))
}
</div>
)
}
Это не удастся, потому что ContainerCards не ожидает этих свойств.
Поэтому нам придется изменить интерфейс ContainerCards.
Файл src/components/ContainerCards.tsx
.
interface Props {
items: Data[]
status: Status
isDragging: boolean
handleDragging: (dragging: boolean) => void
}
И одним движением мы получаем эти реквизиты.
-
Обратите внимание, что в className div мы помещаем условие, где если isDragging равен true, то мы добавляем класс
layout-dragging
. Этот класс будет изменять цвет фона и границы контейнера только при перетаскивании карточки. -
Обратите внимание, что мы также передаем CardItem новое свойство handleDragging, потому что карточка является компонентом, который будет обновлять состояние, созданное нами ранее.
import { CardItem } from "./CardItem"
import { Data, Status } from "../interfaces"
interface Props {
items: Data[]
status: Status
isDragging: boolean
handleDragging: (dragging: boolean) => void
}
export const ContainerCards = ({ items = [], status, isDragging, handleDragging }: Props) => {
return (
<div
className={`layout-cards ${isDragging ? 'layout-dragging' : ''}`}
>
<p>{status} hero</p>
{
items.map(item => (
status === item.status
&& <CardItem
data={item}
key={item.id}
handleDragging={handleDragging}
/>
))
}
</div>
)
}
CardItem выдаст нам ошибку, потому что он не ожидает свойства handleDragging, поэтому мы должны изменить его интерфейс.
Теперь в файле src/components/CardItem.tsx
мы изменим интерфейс
interface Props {
data: Data,
handleDragging: (dragging: boolean) => void
}
И теперь да, мы начинаем добавлять функциональность перетаскивания в этот компонент.
Сначала в div
, который представляет собой всю карточку, мы добавляем атрибут draggable, чтобы указать, что этот компонент можно перетаскивать.
import { Data } from "../interfaces"
interface Props {
data: Data,
handleDragging: (dragging: boolean) => void
}
export const CardItem = ({ data, handleDragging }: Props) => {
return (
<div
className='card-container'
draggable
>
<p>{data.content}</p>
</div>
)
}
Затем мы добавляем атрибут onDragEnd, который будет выполнять функцию handleDragEnd.
Эта функция только установит значение статуса isDraging в false, потому что при выполнении onDragEnd карта больше не будет перетаскиваться, поэтому мы должны удалить стили при перетаскивании, то есть вернуть все стили, как в начале.
import { Data } from "../interfaces"
interface Props {
data: Data,
handleDragging: (dragging: boolean) => void
}
export const CardItem = ({ data, handleDragging }: Props) => {
const handleDragEnd = () => handleDragging(false)
return (
<div
className='card-container'
draggable
onDragEnd={handleDragEnd}
>
<p>{data.content}</p>
</div>
)
}
Затем добавляем атрибут onDragStart (он выполняется, когда компонент начинает перетаскиваться, если бы мы не добавили атрибут draggable, то onDragStart не выполнялся бы).
onDragStart выполнит функцию handleDragStart.
Эта функция получает событие, и внутри события есть свойство, которое нас интересует, это dataTransfer.
Свойство dataTransfer позволяет нам содержать или получать данные при перетаскивании элемента.
Свойство setData в свойстве dataTransfer устанавливает данные, которые мы хотим сохранить при перетаскивании элемента, и принимает два параметра:
-
format: формат сохраняемых данных, который является «text».
-
data: информация, которую мы хотим сохранить при перетаскивании элемента. Он принимает только строку. В данном случае мы будем хранить id карточки.
ПРИМЕЧАНИЕ: внутри dataTransfer также есть свойство clearData, которое очищает кэш данных, которые мы храним. В данном случае выполнять его не нужно, так как мы будем перезаписывать тот же идентификатор ‘text’.
После сохранения данных мы выполняем handleDragging, передавая значение true, чтобы указать пользователю, что мы перетаскиваем элемент.
import { Data } from "../interfaces"
interface Props {
data: Data,
handleDragging: (dragging: boolean) => void
}
export const CardItem = ({ data, handleDragging }: Props) => {
const handleDragStart = (e: React.DragEvent<HTMLDivElement>) => {
e.dataTransfer.setData('text', `${data.id}`)
handleDragging(true)
}
const handleDragEnd = () => handleDragging(false)
return (
<div
className='card-container'
draggable
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
>
<p>{data.content}</p>
</div>
)
}
Таким образом, у нас есть часть перетаскивания элемента, у нас уже есть содержащаяся информация, готовая к тому, чтобы получить ее, когда мы бросим ее в другой контейнер.
Вот как это будет выглядеть, когда мы перетащим карточку, это изменит дизайн контейнеров, указывая, что они являются местами, куда вы можете бросить карточку.
👉 Выполнение сброса.
Прежде чем мы выполним часть освобождения элемента, мы должны сначала сделать другие вещи.
🟠 Создание состояния для хранения карт.
Сначала нужно создать список героев в состоянии и иметь возможность обновить его, когда карта будет сброшена в другой контейнер, в этот момент мы обновим свойство status героя, что приведет к тому, что список будет отрисован снова, организуя карты, которые изменились.
Для этого перейдем в src/components/DragAndDrop.tsx
и создадим новый статус.
Его начальным значением будут данные, которые мы ранее определили в src/assets
.
import { data } from "../assets"
const [listItems, setListItems] = useState<Data[]>(data)
И теперь при рендеринге компонента ContainerCards вместо того, чтобы передавать значение data реквизиту items, мы будем передавать ему значение состояния listItems.
import { ContainerCards } from "./ContainerCards"
import { data } from "../assets"
import { Status } from "../interfaces"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
const [isDragging, setIsDragging] = useState(false)
const [listItems, setListItems] = useState<Data[]>(data)
const handleDragging = (dragging: boolean) => setIsDragging(dragging)
return (
<div className="grid">
{
typesHero.map(container => (
<ContainerCards
items={listItems}
status={container}
key={container}
isDragging={isDragging}
handleDragging={handleDragging}
/>
))
}
</div>
)
}
Затем мы создадим функцию для обновления состояния listItems.
Мы назовем ее handleUpdateList, и она будет принимать два параметра:
- id: идентификатор карточки, он будет иметь тип number.
- status: новый статус карточки, он будет типа Status.
Внутри функции …
1 — Сначала мы будем искать элемент в значении статуса listItems, по идентификатору.
2 — Мы оценим, существуют ли эти данные, и если переданный нам статус отличается от того, который он уже имеет, то мы внесем изменения в статус.
3 — В рамках условия мы обращаемся к найденной карточке и обновляем ее свойство status, присваивая ей новый статус, который передается нам параметром в функции.
4 — Вызываем setListItems для обновления статуса, размещаем:
-
Карточку с ее обновленным свойством статуса.
-
Новый массив, фильтруя элементы, чтобы удалить обновляемую карточку и избежать дублирования информации.
Теперь в компонент ContainerCards добавим новое свойство handleUpdateList и отправим ему только что созданную функцию handleUpdateList.
import { ContainerCards } from "./ContainerCards"
import { data } from "../assets"
import { Status } from "../interfaces"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
const [isDragging, setIsDragging] = useState(false)
const [listItems, setListItems] = useState<Data[]>(data)
const handleDragging = (dragging: boolean) => setIsDragging(dragging)
const handleUpdateList = (id: number, status: Status) => {
let card = listItems.find(item => item.id === id)
if (card && card.status !== status) {
card.status = status
setListItems( prev => ([
card!,
...prev.filter(item => item.id !== id)
]))
}
}
return (
<div className="grid">
{
typesHero.map(container => (
<ContainerCards
items={listItems}
status={container}
key={container}
isDragging={isDragging}
handleDragging={handleDragging}
handleUpdateList={handleUpdateList}
/>
))
}
</div>
)
}
Это даст нам ошибку, потому что компонент ContainerCards не ожидает свойства handleUpdateList, поэтому мы должны обновить интерфейс ContainerCards.
В src/components/ContainerCards.tsx
:
interface Props {
items: Data[]
status: Status
isDragging: boolean
handleDragging: (dragging: boolean) => void
handleUpdateList: (id: number, status: Status) => void
}
👉 Выполняем функции, чтобы сделать падение в контейнерах.
Мы находимся в src/components/ContainerCards.tsx
.
Внутри компонента мы собираемся установить два новых свойства для элемента div.
-
onDragOver*: происходит, когда перетаскиваемый элемент перетаскивается через допустимую цель падения. Мы передаем ему функцию **handleDragOver*, которую создадим через некоторое время.
-
onDrop*: происходит, когда перетаскиваемый элемент опускается. Мы передаем ему функцию **handleDrop*, которую мы создадим в ближайшее время.
<div
className={`layout-cards ${isDragging ? 'layout-dragging' : ''}`}
onDragOver={handleDragOver}
onDrop={handleDrop}
>
<p>{status} hero</p>
{
items.map(item => (
status === item.status
&& <CardItem
data={item}
key={item.id}
handleDragging={handleDragging}
/>
))
}
</div>
Функция handleDragOver будет делать только это.
Во-первых, она получит событие, которое испускает onDragOver.
Поскольку по умолчанию данные не могут быть сброшены на другие элементы, и чтобы разрешить сброс, мы должны избежать поведения по умолчанию.
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault()
}
Теперь функция handleDrop
-
Во-первых, вы получите событие, которое испускает функция onDrop.
-
Внутри функции мы избегаем поведения по умолчанию, которое более заметно с изображениями (когда мы бросаем изображение в каком-либо месте нашего приложения, оно открывается, выводя нас из приложения).
-
Затем из события мы получаем свойство dataTransfer и через свойство getData свойства dataTransfer выполняем его, отправляя идентификатор, из которого мы получим ID карты.
- Знак
+
в началеe.dataTransfer.getData('text')
служит для преобразования значения в число.
- Знак
-
Затем мы вызовем функцию handleUpdateList, которую компонент передает нам по реквизитам (мы должны разгруппировать ее от компонента).
- Сначала мы передадим ей id, который мы получили из свойства getData в dataTransfer, уже преобразованный в число.
- Затем мы передаем ему статус, который мы получили из props в компоненте.
-
Наконец, мы вызываем handleDragging, передавая значение false, чтобы указать пользователю, что мы больше ничего не перетаскиваем.
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault()
const id = +e.dataTransfer.getData('text')
handleUpdateList(id, status)
handleDragging(false)
}
Вот как будет выглядеть код в src/components/ContainerCards.tsx
:
import { Data, Status } from "../interfaces"
import { CardItem } from "./CardItem"
interface Props {
items: Data[]
status: Status
isDragging: boolean
handleUpdateList: (id: number, status: Status) => void
handleDragging: (dragging: boolean) => void
}
export const ContainerCards = ({ items = [], status, isDragging, handleDragging, handleUpdateList }: Props) => {
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault()
handleUpdateList(+e.dataTransfer.getData('text'), status)
handleDragging(false)
}
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => e.preventDefault()
return (
<div
className={`layout-cards ${isDragging ? 'layout-dragging' : ''}`}
onDrop={handleDrop}
onDragOver={handleDragOver}
>
<p>{status} hero</p>
{
items.map(item => (
status === item.status
&& <CardItem
data={item}
key={item.id}
handleDragging={handleDragging}
/>
))
}
</div>
)
}
Конечный результат должен выглядеть следующим образом 🥳!
👉 Дополнительно. Рефакторинг кода в DragAndDrop.tsx
У нас достаточно логики в нашем компоненте, поэтому хорошим вариантом будет создание пользовательского хука для управления этой логикой.
Создадим папку src/hooks
и внутри нее файл под названием useDragAndDrop.ts
.
Сначала мы определим функцию, которая будет получать начальное состояние, имеющее тип Data array.
export const useDragAndDrop = (initialState: Data[]) => {}
Из компонента DragAndDrop.tsx мы вырезаем всю логику и помещаем ее в пользовательский хук.
Начальным значением состояния listItems будет то, которое передано нам параметром хука.
И, наконец, мы возвращаем как объект:
- isDragging.
- listItems.
- handleUpdateList.
- handleDragging.
import { useState } from "react"
import { Data, Status } from "../interfaces"
export const useDragAndDrop = (initialState: Data[]) => {
const [isDragging, setIsDragging] = useState(false)
const [listItems, setListItems] = useState<Data[]>(initialState)
const handleUpdateList = (id: number, status: Status) => {
let card = listItems.find(item => item.id === id)
if (card && card.status !== status) {
card.status = status
setListItems( prev => ([
card!,
...prev.filter(item => item.id !== id)
]))
}
}
const handleDragging = (dragging: boolean) => setIsDragging(dragging)
return {
isDragging,
listItems,
handleUpdateList,
handleDragging,
}
}
Теперь в компоненте src/components/DragAndDrop.tsx
мы вызываем наш пользовательский хук.
Мы отправляем данные в наш хук с помощью параметра и просто разгруппировываем свойства и все.
import { ContainerCards } from "./ContainerCards"
import { useDragAndDrop } from "../hooks/useDragAndDrop"
import { Status } from "../interfaces"
import { data } from "../assets"
const typesHero: Status[] = ['good', 'normal', 'bad']
export const DragAndDrop = () => {
const { isDragging, listItems, handleDragging, handleUpdateList } = useDragAndDrop(data)
return (
<div className="grid">
{
typesHero.map(container => (
<ContainerCards
items={listItems}
status={container}
key={container}
isDragging={isDragging}
handleDragging={handleDragging}
handleUpdateList={handleUpdateList}
/>
))
}
</div>
)
}
Это сделает ваш компонент более читабельным. 🎉
👉 Заключение.
Этот процесс является одним из способов создания приложения с функцией Drag & Drop без использования внешних библиотек.
-
Одним из способов улучшить это приложение было бы использование менеджера состояний, чтобы избежать передачи слишком большого количества реквизитов компонентам.
-
Если вы хотите что-то более сложное и расширить функциональность, вы можете использовать сторонний пакет, который я очень рекомендую, и это
react-beautiful-dnd
, очень хорошая и популярная библиотека.
Надеюсь, я помог вам понять, как выполнить это упражнение, спасибо большое, что дошли до конца! 🤗❤️
Я приглашаю вас прокомментировать, если вы считаете эту статью полезной или интересной, или если вы знаете какой-либо другой или лучший способ, как сделать drag&drop. 🙌
🟠 Live demo.
https://drag-and-drop-react-app.netlify.app
🟠 Исходный код.
Franklin361 / drag-and-drop-react
Создание приложения с помощью Drag & Drop с React JS 🤏
Создание приложения с помощью Drag and Drop с React без библиотек 👆!
На этот раз мы собираемся реализовать функциональность для выполнения Drag & Drop с помощью React JS и без каких-либо других внешних пакетов или библиотек!
- Перетаскивание карточек.
- Сбрасывание карточек в контейнер.
- Сортировка карточек.
- React JS
- TypeScript
- Vite JS
- Vanilla CSS 3
- Клонируйте репозиторий (у вас должен быть установлен Git).
git clone https://github.com/Franklin361/drag-and-drop-react
- Установите зависимости проекта.
npm install
- Запустите проект.
npm run dev