كيفية استخدام واجهة برمجة تطبيقات السحب والإفلات بتنسيق HTML في React
نشرت: 2022-03-10تعد واجهة برمجة تطبيقات السحب والإفلات واحدة من أروع ميزات HTML. يساعدنا في تنفيذ ميزات السحب والإفلات في متصفحات الويب.
في السياق الحالي ، سنقوم بسحب الملفات من خارج المتصفح. عند إسقاط الملف (الملفات) ، نضعها في قائمة ونعرض أسمائها. مع وجود الملفات في متناول اليد ، يمكننا بعد ذلك إجراء بعض العمليات الأخرى على الملف (الملفات) ، على سبيل المثال تحميلها على خادم سحابي.
في هذا البرنامج التعليمي ، سنركز على كيفية تنفيذ إجراء السحب والإفلات في تطبيق React. إذا كان ما تحتاجه هو تطبيق JavaScript
عادي ، فربما ترغب أولاً في قراءة "كيفية إنشاء برنامج تحميل ملفات السحب والإفلات باستخدام Vanilla JavaScript" ، وهو برنامج تعليمي ممتاز كتبه جوزيف زيمرمان منذ وقت ليس ببعيد.
dragenter
، dragleave
، dragover
، drop
الأحداث
هناك ثمانية أحداث سحب وإفلات مختلفة. كل واحد يتم إطلاقه في مرحلة مختلفة من عملية السحب والإفلات. في هذا البرنامج التعليمي ، سنركز على الأربعة التي يتم إطلاقها عند إسقاط عنصر ما في منطقة الإسقاط: dragenter
، dragleave
، dragover
، drop
.
- يتم تشغيل الحدث
dragenter
عندما يدخل عنصر تم سحبه في هدف إسقاط صالح. - يتم تشغيل الحدث
dragleave
عندما يترك عنصر تم سحبه هدف إسقاط صالح. - يتم
dragover
حدث السحب عندما يتم سحب عنصر تم سحبه فوق هدف إسقاط صالح. (تنطلق كل بضع مئات من الألف من الثانية). - يتم تشغيل حدث
drop
عندما يسقط عنصر على هدف إسقاط صالح ، أي يتم سحبه فوق وإطلاقه.
يمكننا تحويل أي عنصر HTML إلى هدف إسقاط صالح عن طريق تحديد سمات معالج الأحداث ondragover
و ondrop
.
يمكنك معرفة كل شيء عن الأحداث الثمانية من مستندات الويب MDN.
أحداث السحب والإفلات في React
للبدء ، قم باستنساخ الريبو التعليمي من عنوان URL هذا:
https://github.com/chidimo/react-dnd.git
تحقق من فرع 01-start
Start. تأكد من تثبيت yarn
لديك أيضًا. يمكنك الحصول عليه من yarnpkg.com.
ولكن إذا كنت تفضل ذلك ، فقم بإنشاء مشروع React جديد واستبدل محتوى App.js بالكود أدناه:
import React from 'react'; import './App.css'; function App() { return ( <div className="App"> <h1>React drag-and-drop component</h1> </div> ); } export default App;
أيضًا ، استبدل محتوى App.css بنمط CSS أدناه:
.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; }
إذا قمت باستنساخ الريبو ، فقم بإصدار الأوامر التالية (بالترتيب) لبدء التطبيق:
yarn # install dependencies yarn start # start the app
الخطوة التالية هي إنشاء مكون السحب والإفلات. قم بإنشاء ملف DragAndDrop.js داخل src/
المجلد. أدخل الوظيفة التالية داخل الملف:
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;
في قسم div
، حددنا سمات معالج حدث HTML
للتركيز. يمكنك أن ترى أن الاختلاف الوحيد عن HTML
الخالص هو غلاف الجمل.
أصبح div
الآن هدف إسقاط صالحًا نظرًا لأننا حددنا سمات معالج الحدث onDragOver
و onDrop
.
لقد حددنا أيضًا وظائف للتعامل مع تلك الأحداث. تستقبل كل دالة من وظائف المعالج كائن الحدث كوسيطة لها.
لكل من معالجات الأحداث ، نطلق على preventDefault()
منع المتصفح من تنفيذ سلوكه الافتراضي. سلوك المتصفح الافتراضي هو فتح الملف الذي تم إسقاطه. نقوم أيضًا باستدعاء stopPropagation()
للتأكد من أن الحدث لا يتم نشره من العناصر الفرعية إلى العناصر الأصل.
قم باستيراد مكون DragAndDrop
إلى مكون App
وعرضه أسفل العنوان.
<div className="App"> <h1>React drag-and-drop component</h1> <DragAndDrop /> </div>
شاهد الآن المكون في المتصفح وسترى شيئًا مثل الصورة أدناه.
إذا كنت تتابع الريبو ، فإن الفرع المقابل هو 02-start-dragndrop
إدارة الدولة باستخدام useReducer
ستكون خطوتنا التالية هي كتابة المنطق لكل من معالجات الأحداث لدينا. قبل أن نفعل ذلك ، علينا أن نفكر في الكيفية التي ننوي بها تتبع الملفات التي تم إسقاطها. هذا هو المكان الذي نبدأ فيه التفكير في إدارة الدولة.
سنتعقب الحالات التالية أثناء عملية السحب والإفلات:
-
dropDepth
سيكون هذا عددًا صحيحًا. سنستخدمه لتتبع عدد المستويات الموجودة في عمق منطقة الإسقاط. في وقت لاحق ، سأشرح هذا مع توضيح. ( قروض لإيجور إيجوروف لإلقاء الضوء على هذا من أجلي! ) -
inDropZone
سيكون هذا منطقيًا. سنستخدم هذا لتتبع ما إذا كنا داخل منطقة الإسقاط أم لا. -
FileList
ستكون هذه قائمة. سنستخدمه لتتبع الملفات التي تم إسقاطها في منطقة الإسقاط.
للتعامل مع الحالات ، توفر useState
الخطاف useState و useReducer
. سنختار الخطاف useReducer
نظرًا لأننا سنتعامل مع المواقف التي تعتمد فيها الحالة على الحالة السابقة.
يقبل الخطاف useReducer
من النوع (state, action) => newState
، ويعيد الحالة الحالية المقترنة بطريقة dispatch
.
يمكنك قراءة المزيد عن useReducer
في مستندات React.
داخل مكون App
(قبل بيان return
) ، أضف الكود التالي:
... 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
: مخفض وحالة أولية. تقوم بإرجاع الحالة الحالية ووظيفة dispatch
التي يتم من خلالها تحديث الحالة. يتم تحديث الحالة عن طريق إرسال إجراء يحتوي على type
وحمولة اختيارية. يعتمد التحديث الذي تم إجراؤه على حالة المكون على ما يتم إرجاعه من بيان الحالة كنتيجة لنوع الإجراء. (لاحظ هنا أن حالتنا الأولية هي object
.)
لكل من متغيرات الحالة ، قمنا بتعريف بيان الحالة المقابل لتحديثه. يتم إجراء التحديث من خلال استدعاء وظيفة dispatch
التي يتم إرجاعها بواسطة useReducer
.
الآن قم بتمرير data
dispatch
DragAndDrop
props
ملف App.js الخاص بك:
<DragAndDrop data={data} dispatch={dispatch} />
في الجزء العلوي من مكون DragAndDrop
، يمكننا الوصول إلى كلتا القيمتين من props
.
const { data, dispatch } = props;
إذا كنت تتابع الريبو ، فإن الفرع المقابل هو 03-define-reducers
.
لننتهي من منطق معالجات الأحداث. لاحظ أن علامة القطع تمثل السطرين:
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 و B. A هي منطقة اهتمامنا. هذا هو المكان الذي نريد الاستماع إلى أحداث السحب والإفلات.
عند السحب إلى منطقة الإسقاط ، في كل مرة نصل فيها إلى حد ، يتم إطلاق حدث ondragenter
. يحدث هذا عند الحدود A-in
و B-in
. نظرًا لأننا ندخل المنطقة ، dropDepth
.
وبالمثل ، عند السحب من منطقة الإسقاط ، في كل مرة نصل فيها إلى حد ، يتم إطلاق حدث ondragleave
. يحدث هذا عند الحدود A-out
و B-out
. نظرًا لأننا نغادر المنطقة ، فإننا نقلل من قيمة dropDepth
. لاحظ أننا لم نقم بتعيين inDropZone
على false
عند الحد B-out
. لهذا السبب لدينا هذا الخط للتحقق من عمق الإسقاط والعودة من وظيفة dropDepth
أكبر من 0
.
if (data.dropDepth > 0) return
هذا لأنه على الرغم من إطلاق حدث ondragleave
، إلا أننا ما زلنا داخل المنطقة A. فقط بعد أن نصل إلى A-out
، وأصبح dropDepth
الآن 0
، قمنا بتعيين inDropZone
على false
. في هذه المرحلة ، تركنا جميع مناطق الإسقاط.
const handleDragOver = e => { ... e.dataTransfer.dropEffect = 'copy'; dispatch({ type: 'SET_IN_DROP_ZONE', inDropZone: true }); };
في كل مرة يتم فيها إطلاق هذا الحدث ، نقوم بتعيين inDropZone
على " true
". يخبرنا هذا أننا داخل منطقة الإسقاط. قمنا أيضًا بتعيين dropEffect
على كائن dataTransfer
copy
. على جهاز Mac ، يكون لهذا تأثير إظهار علامة الجمع الخضراء أثناء قيامك بسحب عنصر في منطقة الإسقاط.
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 }); } };
يمكننا الوصول إلى الملفات التي تم إسقاطها باستخدام e.dataTransfer.files
. القيمة عبارة عن كائن يشبه المصفوفة ، لذلك نستخدم صيغة انتشار المصفوفة لتحويلها إلى مصفوفة JavaScript
.
نحتاج الآن إلى التحقق مما إذا كان هناك ملف واحد على الأقل قبل محاولة إضافته إلى مجموعة الملفات الخاصة بنا. نتأكد أيضًا من عدم تضمين الملفات الموجودة بالفعل في قائمة الملفات الخاصة fileList
. يتم مسح كائن dataTransfer
استعدادًا لعملية السحب والإفلات التالية. نقوم أيضًا بإعادة تعيين قيم dropDepth
و inDropZone
.
قم بتحديث className
الخاص بـ div
في مكون DragAndDrop
. سيؤدي هذا إلى تغيير اسم div
className
لقيمة data.inDropZone
.
<div className={data.inDropZone ? 'drag-drop-zone inside-drag-area' : 'drag-drop-zone'} ... > <p>Drag files here to upload</p> </div>
اعرض قائمة الملفات في App.js عن طريق التعيين عبر data.fileList
.
<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>
حاول الآن سحب بعض الملفات وإفلاتها في منطقة الإسقاط. ستلاحظ أنه عند دخولنا منطقة الإسقاط ، تصبح الخلفية أقل تعتيمًا نظرًا لتنشيط فئة inside-drag-area
.
عند تحرير الملفات داخل منطقة الإسقاط ، سترى أسماء الملفات مدرجة ضمن منطقة الإسقاط:
النسخة الكاملة من هذا البرنامج التعليمي موجودة في فرع 04-finish-handlers
.
خاتمة
لقد رأينا كيفية التعامل مع تحميلات الملفات في React باستخدام واجهة برمجة تطبيقات السحب والإفلات بتنسيق HTML
. لقد تعلمنا أيضًا كيفية إدارة الحالة باستخدام الخطاف useReducer
. يمكننا تمديد وظيفة handleDrop
الملف. على سبيل المثال ، يمكننا إضافة فحص آخر لتحديد أحجام الملفات إذا أردنا ذلك. يمكن أن يأتي هذا قبل أو بعد التحقق من الملفات الموجودة. يمكننا أيضًا جعل منطقة الإسقاط قابلة للنقر دون التأثير على وظيفة السحب والإفلات.
موارد
- "مرجع واجهة برمجة تطبيقات Hooks:
useReducer
" ، React Docs - "HTML Drag-and-Drop API" ، مستندات الويب MDN
- "أمثلة على تطوير الويب و XML باستخدام DOM ،" مستندات الويب MDN
- "كيفية إنشاء برنامج تحميل ملفات السحب والإفلات باستخدام Vanilla JavaScript ،" جوزيف زيمرمان ، مجلة Smashing
- "تحميل ملف سحب وإفلات بسيط في رد فعل" ، إيجور إيجوروف ، متوسط