Перетаскивание приложений очень распространено в наши дни, оно отлично подходит для пользовательского опыта внутри приложения. И вы, вероятно, хотели бы реализовать его в своем следующем проекте.
На этот раз я покажу вам, как сделать приложение с функцией drag & drop, но без использования каких-либо внешних библиотек, только с помощью React JS.
Примечание: Этот пост требует от вас знания основ React с TypeScript (базовые хуки и пользовательские хуки).
Любой вид обратной связи приветствуется, спасибо и я надеюсь, что вам понравится статья.🤗
- Оглавление.
- 👉 Используемые технологии.
- 👉 Создание проекта.
- 👉 Первые шаги.
- 👉 Создание наших открыток.
- 👉 Создание контейнеров для наших открыток.
- Определение типа и интерфейса для информации о карте.
- 👉 Создание компонента DragAndDrop.tsx
- 👉 Добавление некоторых данных для создания карточек.
- 👉 Показываю карты.
- 👉 Выполнение функции перетаскивания.
- 👉 Выполнение функции Drop.
- 🟠 Создание состояния для хранения карт.
- 👉 Выполнение функций по опусканию в контейнеры.
- 👉 Необязательно. Рефакторинг кода в DragAndDrop.tsx
- 👉 Заключение.
- 🟠 Живая демонстрация.
- 🟠 Исходный код.
- Franklin361 / drag-and-drop-react
- Создание приложения с помощью Drag & Drop с React JS 🤏
Оглавление.
📌 Технологии для использования.
📌 Создание проекта.
📌 Первые шаги.
📌 Создание наших открыток.
📌 Создание контейнеров для наших открыток.
📌 Определение типа и интерфейса для информации карт.
📌 Создание компонента DragAndDrop.tsx.
📌 Добавление некоторых данных для создания карточек.
📌 Показываю карты.
📌 Выполнение функции перетаскивания.
📌 Выполнение функции Drop.
📌 Создание государства для хранения карт.
📌 Выполнение функций по загрузке контейнеров.
📌 Необязательно. Рефакторинг кода в
DragAndDrop.tsx
.📌 Заключение.
📌 Живая демонстрация.
📌 Исходный код.
👉 Используемые технологии.
- ▶️ React JS (версия 18).
- ▶️ Vite JS
- ▶️ TypeScript
- ▶️ CSS-ваниль (стили можно найти в репозитории в конце этого поста)
👉 Создание проекта.
Мы назовем проект: 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
и внутри add:
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, этому prop мы передаем в качестве значения данные, которые мы создали в папке 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, которые мы определили ранее.
Сразу же мы показываем свойство content внутри данных.
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
мы изменяем интерфейс Props, добавляя свойство 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>
)
}
Это даст вам предупреждение о том, что ключи повторяются 😥.
Это происходит потому, что мы визуализируем ContainerCards 3 раза.
Но подождите, единственное свойство, которое сделает разницу между этими 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, так как в начале работы приложения оно не будет перетаскиваться.
- Это будет верно только при перетаскивании карты.
-
Функция, которая получает булево значение, поможет нам изменить значение на состояние, это делается для того, чтобы избежать передачи сеттера 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.
Эта функция только установит значение состояния isDragging в 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: это информация, которую мы хотим сохранить при перетаскивании элемента. Он принимает только строку. В этом случае мы будем хранить идентификатор карты.
ПРИМЕЧАНИЕ: внутри 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>
)
}
Таким образом, у нас будет часть перетаскивания элемента, и у нас будет содержащаяся в нем информация, готовая к получению, когда мы перетащим его в другой контейнер.
Вот как это будет выглядеть, когда мы перетаскиваем карту, это изменяет дизайн контейнеров, указывая, что они являются местами, куда можно опустить карту.
👉 Выполнение функции Drop.
Прежде чем мы сможем выполнить часть элемента, связанную с падением, нам нужно сначала сделать некоторые другие вещи.
🟠 Создание состояния для хранения карт.
Сначала установим список героев в состояние и сможем обновить его, когда карта будет сброшена в другой контейнер, в это время мы обновим свойство 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: идентификатор карты, он будет иметь тип номер.
- 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, которую компонент передает нам по props, (вы должны отстроить ее от компонента).
- Сначала мы передаем ему id, который мы получили из свойства getData в dataTransfer, уже преобразованный в число.
- Затем мы передаем статус, который мы получили с помощью реквизита, в компонент.
-
Наконец, мы вызываем 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
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. 🙌
🟠 Живая демонстрация.
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
- Ванильный CSS 3
- Клонируйте репозиторий (у вас должен быть установлен Git).
git clone https://github.com/Franklin361/drag-and-drop-react
- Установите зависимости проекта.
npm install
- Запустите проект.
npm run dev