React'te HTML Sürükle ve Bırak API'si Nasıl Kullanılır
Yayınlanan: 2022-03-10Sürükle ve bırak API'si, HTML'nin en havalı özelliklerinden biridir. Web tarayıcılarında sürükle ve bırak özelliklerini uygulamamıza yardımcı olur.
Mevcut bağlamda, dosyaları tarayıcının dışından sürükleyeceğiz. Dosyayı/dosyaları bıraktığımızda, onları bir listeye koyarız ve isimlerini gösteririz. Dosyalar elimizdeyken, dosya(lar) üzerinde başka bir işlem gerçekleştirebiliriz, örneğin onları bir bulut sunucusuna yükleyebiliriz.
Bu eğitimde, bir React uygulamasında sürükleyip bırakma eyleminin nasıl uygulanacağına odaklanacağız. İhtiyacınız olan şey basit bir JavaScript
uygulamasıysa, belki de kısa süre önce Joseph Zimmerman tarafından yazılmış mükemmel bir öğretici olan “Vanilla JavaScript ile Sürükle ve Bırak Dosya Yükleyici Nasıl Yapılır”ı okumak istersiniz.
dragenter
, dragleave
, dragover
ve drop
Olayları
Sekiz farklı sürükle ve bırak olayı vardır. Her biri, sürükle ve bırak işleminin farklı bir aşamasında tetiklenir. Bu öğreticide, bir öğe bırakma bölgesine bırakıldığında tetiklenen dördüne odaklanacağız: dragenter
, dragleave
, dragover
ve drop
.
- Sürüklenen bir öğe geçerli bir bırakma hedefine girdiğinde
dragenter
olayı tetiklenir. - Sürüklenen bir öğe geçerli bir bırakma hedefi bıraktığında
dragleave
olayı tetiklenir. - Sürüklenen bir öğe geçerli bir bırakma hedefi üzerine sürüklenirken sürüklenme olayı
dragover
. (Her birkaç yüz milisaniyede bir ateşlenir.) -
drop
olayı, bir öğe geçerli bir bırakma hedefine düştüğünde, yani üzerine sürüklenip bırakıldığında tetiklenir.
ondragover
ve ondrop
olay işleyici niteliklerini tanımlayarak herhangi bir HTML öğesini geçerli bir bırakma hedefine dönüştürebiliriz.
Sekiz olayla ilgili her şeyi MDN web dokümanlarından öğrenebilirsiniz.
Tepkideki Sürükle ve Bırak Olayları
Başlamak için eğitim deposunu bu URL'den kopyalayın:
https://github.com/chidimo/react-dnd.git
01-start
şubesine bakın. yarn
de takılı olduğundan emin olun. iplikpkg.com'dan temin edebilirsiniz.
Ancak isterseniz yeni bir React projesi oluşturun ve App.js'nin içeriğini aşağıdaki kodla değiştirin:
import React from 'react'; import './App.css'; function App() { return ( <div className="App"> <h1>React drag-and-drop component</h1> </div> ); } export default App;
Ayrıca, App.css içeriğini aşağıdaki CSS stiliyle değiştirin:
.App { margin: 2rem; text-align: center; } h1 { color: #07F; } .drag-drop-zone { padding: 2rem; text-align: center; background: #07F; border-radius: 0.5rem; box-shadow: 5px 5px 10px #C0C0C0; } .drag-drop-zone p { color: #FFF; } .drag-drop-zone.inside-drag-area { opacity: 0.7; } .dropped-files li { color: #07F; padding: 3px; text-align: left; font-weight: bold; }
Depoyu klonladıysanız, uygulamayı başlatmak için aşağıdaki komutları (sırasıyla) verin:
yarn # install dependencies yarn start # start the app
Bir sonraki adım, bir sürükle ve bırak bileşeni oluşturmaktır. src/
klasörü içinde bir DragAndDrop.js dosyası oluşturun. Dosyanın içine aşağıdaki işlevi girin:
import React from 'react'; const DragAndDrop = props => { const handleDragEnter = e => { e.preventDefault(); e.stopPropagation(); }; const handleDragLeave = e => { e.preventDefault(); e.stopPropagation(); }; const handleDragOver = e => { e.preventDefault(); e.stopPropagation(); }; const handleDrop = e => { e.preventDefault(); e.stopPropagation(); }; return ( <div className={'drag-drop-zone'} onDrop={e => handleDrop(e)} onDragOver={e => handleDragOver(e)} onDragEnter={e => handleDragEnter(e)} onDragLeave={e => handleDragLeave(e)} > <p>Drag files here to upload</p> </div> ); }; export default DragAndDrop;
Return div
, odak HTML
olay işleyici özniteliklerimizi tanımladık. Saf HTML
tek farkın deve kasası olduğunu görebilirsiniz.
onDragOver
ve onDrop
olay işleyici özniteliklerini tanımladığımız için div
artık geçerli bir bırakma hedefidir.
Ayrıca bu olayları işlemek için işlevler tanımladık. Bu işleyici işlevlerinin her biri, olay nesnesini argümanı olarak alır.
Olay işleyicilerin her biri için, tarayıcının varsayılan davranışını yürütmesini durdurmak için preventDefault()
'u çağırırız. Varsayılan tarayıcı davranışı, bırakılan dosyayı açmaktır. Ayrıca olayın alt öğeden üst öğeye yayılmadığından emin olmak için stopPropagation()
öğesini çağırırız.
DragAndDrop
bileşenini App
bileşenine aktarın ve başlığın altında oluşturun.
<div className="App"> <h1>React drag-and-drop component</h1> <DragAndDrop /> </div>
Şimdi bileşeni tarayıcıda görüntüleyin ve aşağıdaki resim gibi bir şey görmelisiniz.
Depoyu takip ediyorsanız, ilgili dal 02-start-dragndrop
useReducer
Hook ile Durumu Yönetme
Bir sonraki adımımız, olay işleyicilerimizin her biri için mantığı yazmak olacaktır. Bunu yapmadan önce, bırakılan dosyaları nasıl takip etmeyi planladığımızı düşünmeliyiz. Devlet yönetimi hakkında düşünmeye başladığımız yer burasıdır.
Sürükle ve bırak işlemi sırasında aşağıdaki durumları takip edeceğiz:
-
dropDepth
Bu bir tamsayı olacaktır. Düşme bölgesinde kaç seviye derinlikte olduğumuzu takip etmek için kullanacağız. Daha sonra bunu bir örnekle açıklayacağım. ( Benim için bu konuya ışık tuttuğu için Egor Egorov'a teşekkür ederiz! ) -
inDropZone
Bu bir boole olacak. Bunu, düşme bölgesinde olup olmadığımızı takip etmek için kullanacağız. -
FileList
Bu bir liste olacak. Bırakma bölgesine bırakılan dosyaları takip etmek için kullanacağız.
Durumları işlemek için React, useState
ve useReducer
kancalarını sağlar. Bir durumun önceki duruma bağlı olduğu durumlarla ilgileneceğimiz için useReducer
kancasını tercih edeceğiz.
useReducer
kancası (state, action) => newState
türünde bir indirgeyici kabul eder ve bir dispatch
yöntemiyle eşleştirilmiş geçerli durumu döndürür.
useReducer
hakkında daha fazla bilgiyi React belgelerinde okuyabilirsiniz .
App
bileşeninin içine ( return
ifadesinden önce) aşağıdaki kodu ekleyin:
... const reducer = (state, action) => { switch (action.type) { case 'SET_DROP_DEPTH': return { ...state, dropDepth: action.dropDepth } case 'SET_IN_DROP_ZONE': return { ...state, inDropZone: action.inDropZone }; case 'ADD_FILE_TO_LIST': return { ...state, fileList: state.fileList.concat(action.files) }; default: return state; } }; const [data, dispatch] = React.useReducer( reducer, { dropDepth: 0, inDropZone: false, fileList: [] } ) ...
useReducer
kancası iki bağımsız değişkeni kabul eder: bir redüktör ve bir başlangıç durumu. Mevcut durumu ve durumu güncellemek için bir dispatch
işlevi döndürür. Durum, bir type
ve isteğe bağlı bir yük içeren bir eylem gönderilerek güncellenir. Bileşenin durumuna yapılan güncelleme, eylem türünün sonucu olarak case ifadesinden ne döndürüldüğüne bağlıdır. (Burada ilk durumumuzun bir object
olduğuna dikkat edin.)
Durum değişkenlerinin her biri için, onu güncellemek üzere karşılık gelen bir vaka ifadesi tanımladık. Güncelleme, useReducer
tarafından döndürülen dispatch
işlevi çağrılarak gerçekleştirilir.
Şimdi data
DragAndDrop
dispatch
props
olarak gönderin :
<DragAndDrop data={data} dispatch={dispatch} />
DragAndDrop
bileşeninin en üstünde, props
öğesinden her iki değere de erişebiliriz.
const { data, dispatch } = props;
Depoyu takip ediyorsanız, karşılık gelen dal 03-define-reducers
.
Olay işleyicilerimizin mantığını bitirelim. Üç noktanın iki satırı temsil ettiğine dikkat edin:
e.preventDefault() e.stopPropagation() const handleDragEnter = e => { ... dispatch({ type: 'SET_DROP_DEPTH', dropDepth: data.dropDepth + 1 }); }; const handleDragLeave = e => { ... dispatch({ type: 'SET_DROP_DEPTH', dropDepth: data.dropDepth - 1 }); if (data.dropDepth > 0) return dispatch({ type: 'SET_IN_DROP_ZONE', inDropZone: false }) };
Aşağıdaki resimde, A ve B iç içe bırakma bölgelerine sahibiz. A bizim ilgi alanımızdır. Sürükle ve bırak olaylarını dinlemek istediğimiz yer burasıdır.
Bir bırakma bölgesine sürüklerken, bir sınıra her çarptığımızda ondragenter
olayı tetiklenir. Bu, A-in
ve B-in
sınırlarında gerçekleşir. Bölgeye girdiğimiz için dropDepth
değerini artırıyoruz.
Benzer şekilde, bir bırakma bölgesinden dışarı sürüklenirken, bir sınıra her çarptığımızda, ondragleave
olayı tetiklenir. Bu, A-out
ve B-out
sınırlarında gerçekleşir. Bölgeden ayrıldığımız için dropDepth
değerini azaltıyoruz. B-out
sınırında inDropZone
false
olarak ayarlamadığımıza dikkat edin. Bu nedenle dropDepth'i kontrol etmek ve dropDepth
işlevinden 0
büyük dönmek için bu satırı kullanıyoruz.
if (data.dropDepth > 0) return
Bunun nedeni, ondragleave
olayının tetiklenmesine rağmen hala A bölgesi içinde olmamızdır. Sadece A-out
'a bastıktan ve dropDepth
şimdi 0
olduğunda inDropZone
false
olarak ayarladık. Bu noktada, tüm bırakma bölgelerinden ayrıldık.
const handleDragOver = e => { ... e.dataTransfer.dropEffect = 'copy'; dispatch({ type: 'SET_IN_DROP_ZONE', inDropZone: true }); };
Bu olay her tetiklendiğinde, inDropZone
true
olarak ayarladık. Bu bize bırakma bölgesinin içinde olduğumuzu söylüyor. Ayrıca dataTransfer
nesnesindeki dropEffect
copy
şekilde ayarladık. Mac'te bu, bırakma bölgesinde bir öğeyi sürüklerken yeşil bir artı işareti gösterme etkisine sahiptir.
const handleDrop = e => { ... let files = [...e.dataTransfer.files]; if (files && files.length > 0) { const existingFiles = data.fileList.map(f => f.name) files = files.filter(f => !existingFiles.includes(f.name)) dispatch({ type: 'ADD_FILE_TO_LIST', files }); e.dataTransfer.clearData(); dispatch({ type: 'SET_DROP_DEPTH', dropDepth: 0 }); dispatch({ type: 'SET_IN_DROP_ZONE', inDropZone: false }); } };
Bırakılan dosyalara e.dataTransfer.files
ile erişebiliriz. Değer, dizi benzeri bir nesnedir, bu nedenle onu bir JavaScript
dizisine dönüştürmek için dizi yayılma sözdizimini kullanırız.
Şimdi, dosya dizimize eklemeye çalışmadan önce en az bir dosya olup olmadığını kontrol etmemiz gerekiyor. Ayrıca, halihazırda fileList
bulunan dosyaları dahil etmemeye özen gösteriyoruz. dataTransfer
nesnesi, sonraki sürükle ve bırak işlemi için hazırlanırken temizlenir. Ayrıca dropDepth
ve inDropZone
değerlerini de sıfırlıyoruz.
DragAndDrop
bileşenindeki div
className
değerini güncelleyin. Bu, data.inDropZone
değerine bağlı olarak div
className
değerini koşullu olarak değiştirir.
<div className={data.inDropZone ? 'drag-drop-zone inside-drag-area' : 'drag-drop-zone'} ... > <p>Drag files here to upload</p> </div>
data.fileList aracılığıyla data.fileList
dosyaların listesini oluşturun.
<div className="App"> <h1>React drag-and-drop component</h1> <DragAndDrop data={data} dispatch={dispatch} /> <ol className="dropped-files"> {data.fileList.map(f => { return ( <li key={f.name}>{f.name}</li> ) })} </ol> </div>
Şimdi bırakma bölgesine bazı dosyaları sürükleyip bırakmayı deneyin. Bırakma bölgesine girdiğimizde, inside-drag-area
sınıfı etkinleştirildiğinden arka planın daha az opak hale geldiğini göreceksiniz.
Bırakma bölgesi içindeki dosyaları serbest bıraktığınızda, bırakma bölgesi altında listelenen dosya adlarını göreceksiniz:
Bu öğreticinin tam sürümü 04-finish-handlers
dalındadır.
Çözüm
HTML
sürükle ve bırak API'sini kullanarak React'te dosya yüklemelerinin nasıl ele alınacağını gördük. Ayrıca useReducer
kancasıyla durumu nasıl yöneteceğimizi de öğrendik. handleDrop
işlevini genişletebiliriz. Örneğin, istersek dosya boyutlarını sınırlamak için başka bir kontrol ekleyebiliriz. Bu, mevcut dosyaların kontrolünden önce veya sonra gelebilir. Ayrıca, sürükle ve bırak işlevini etkilemeden bırakma bölgesini tıklanabilir hale getirebiliriz.
Kaynaklar
- “Hooks API Reference:
useReducer
”, React Docs - "HTML Sürükle ve Bırak API'si", MDN web belgeleri
- "DOM Kullanarak Web ve XML Geliştirme Örnekleri", MDN web docs
- “Vanilla JavaScript ile Sürükle ve Bırak Dosya Yükleyici Nasıl Yapılır,” Joseph Zimmerman, Smashing Magazine
- "React'te Basit Sürükle ve Bırak Dosya Yüklemesi", Egor Egorov, Medium