فهم معالجة الأحداث في React
معالجة الأحداث في React مشابهة لمعالجة الأحداث في JavaScript العادي، ولكن مع بعض الاختلافات المهمة في الصياغة والسلوك. يتم تسمية أحداث React باستخدام camelCase وتمرر دالة كمعالج للحدث بدلاً من سلسلة نصية.
الفرق الرئيسي: في HTML، تكتب onclick="handleClick()"، ولكن في React JSX، تكتب onClick={handleClick} بدون أقواس (إلا إذا كنت تنشئ دالة سهمية مضمنة).
معالجة الأحداث الأساسية
يقوم React بتغليف أحداث المتصفح الأصلية بأحداث اصطناعية لتوافق المتصفحات. إليك كيفية معالجة الأحداث الأساسية:
function Button() {
const handleClick = () => {
console.log('تم النقر على الزر!');
};
const handleMouseEnter = () => {
console.log('دخل الماوس إلى الزر');
};
return (
<button
onClick={handleClick}
onMouseEnter={handleMouseEnter}
>
انقر هنا
</button>
);
}
الأحداث الاصطناعية
نظام الأحداث الاصطناعية في React يغلف أحداث المتصفح الأصلية. للأحداث الاصطناعية نفس واجهة الأحداث الأصلية، بما في ذلك دوال مثل stopPropagation() و preventDefault().
function Form() {
const handleSubmit = (event) => {
event.preventDefault(); // منع إرسال النموذج الافتراضي
console.log('تم إرسال النموذج');
};
const handleInput = (event) => {
// event.target يعطيك عنصر DOM
console.log('قيمة الإدخال:', event.target.value);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleInput}
placeholder="أدخل نصاً"
/>
<button type="submit">إرسال</button>
</form>
);
}
نصيحة للأداء: عرّف معالجات الأحداث خارج دالة الرسم عندما يكون ذلك ممكناً. إنشاء دوال داخل render (مثل الدوال السهمية المضمنة) ينشئ دالة جديدة في كل عرض، مما قد يؤثر على الأداء في التطبيقات الكبيرة.
تمرير المعاملات إلى معالجات الأحداث
عندما تحتاج إلى تمرير معاملات إضافية إلى معالجات الأحداث، لديك عدة خيارات:
function ItemList() {
const items = [
{ id: 1, name: 'تفاح' },
{ id: 2, name: 'موز' },
{ id: 3, name: 'كرز' }
];
// الطريقة 1: دالة سهمية في JSX (تنشئ دالة جديدة في كل عرض)
const handleDelete1 = (id, event) => {
console.log('حذف العنصر:', id);
};
// الطريقة 2: استخدام bind (تنشئ دالة جديدة، ولكن يمكن تحسينها)
const handleDelete2 = function(id, event) {
console.log('حذف العنصر:', id);
};
// الطريقة 3: Currying (موصى بها للسيناريوهات المعقدة)
const handleDelete3 = (id) => (event) => {
console.log('حذف العنصر:', id);
};
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.name}
{/* الطريقة 1: دالة سهمية مضمنة */}
<button onClick={(e) => handleDelete1(item.id, e)}>
حذف 1
</button>
{/* الطريقة 2: استخدام bind */}
<button onClick={handleDelete2.bind(null, item.id)}>
حذف 2
</button>
{/* الطريقة 3: دالة curried */}
<button onClick={handleDelete3(item.id)}>
حذف 3
</button>
</li>
))}
</ul>
);
}
خطأ شائع: لا تستدعي الدالة مباشرة بإضافة أقواس: onClick={handleClick()}. هذا ينفذ الدالة أثناء العرض بدلاً من عند حدوث الحدث. استخدم onClick={handleClick} بدلاً من ذلك.
اصطلاحات تسمية معالجات الأحداث
اتبع هذه الاصطلاحات للحصول على كود أكثر قابلية للقراءة:
function Example() {
// دوال المعالج عادة تبدأ بـ "handle"
const handleClick = () => { /* ... */ };
const handleSubmit = () => { /* ... */ };
const handleChange = () => { /* ... */ };
// للـ props الممررة إلى المكونات الفرعية، استخدم بادئة "on"
return (
<div>
<button onClick={handleClick}>انقر</button>
<CustomButton onButtonClick={handleClick} />
</div>
);
}
function CustomButton({ onButtonClick }) {
// المكون الفرعي يستقبل prop بصيغة "on..."
return <button onClick={onButtonClick}>زر مخصص</button>;
}
أنواع الأحداث الشائعة
يدعم React جميع أحداث DOM القياسية. إليك الأكثر استخداماً:
function EventExamples() {
const handleClick = () => console.log('حدث النقر');
const handleDoubleClick = () => console.log('حدث النقر المزدوج');
const handleMouseOver = () => console.log('حدث تحريك الماوس');
const handleMouseOut = () => console.log('حدث خروج الماوس');
const handleKeyDown = (e) => console.log('المفتاح المضغوط:', e.key);
const handleFocus = () => console.log('تم التركيز على الإدخال');
const handleBlur = () => console.log('تم إلغاء التركيز');
const handleChange = (e) => console.log('تغيرت القيمة:', e.target.value);
return (
<div>
{/* أحداث الماوس */}
<button
onClick={handleClick}
onDoubleClick={handleDoubleClick}
onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut}
>
مرر وانقر
</button>
{/* أحداث لوحة المفاتيح */}
<input
type="text"
onKeyDown={handleKeyDown}
onFocus={handleFocus}
onBlur={handleBlur}
onChange={handleChange}
placeholder="اكتب هنا"
/>
</div>
);
}
انتشار الأحداث وإيقاف الانتشار
تنتشر الأحداث في React عبر شجرة المكونات تماماً كما في DOM. يمكنك إيقاف هذا الانتشار عند الحاجة:
function EventBubbling() {
const handleParentClick = () => {
console.log('تم النقر على الأب');
};
const handleChildClick = (event) => {
event.stopPropagation(); // يمنع تشغيل معالج الأب
console.log('تم النقر على الابن');
};
const handleLinkClick = (event) => {
event.preventDefault(); // يمنع سلوك المتصفح الافتراضي
console.log('تم النقر على الرابط، لكن تم منع التنقل');
};
return (
<div onClick={handleParentClick} style={{ padding: '20px', background: 'lightblue' }}>
<p>الحاوية الأب</p>
<button onClick={handleChildClick}>
زر الابن (لن ينتشر النقر إلى الأب)
</button>
<br />
<a href="https://example.com" onClick={handleLinkClick}>
رابط (تم منع التنقل)
</a>
</div>
);
}
أفضل ممارسة: استخدم event.preventDefault() لمنع سلوك المتصفح الافتراضي (مثل إرسال النموذج أو التنقل عبر الرابط)، و event.stopPropagation() لمنع انتشار الحدث إلى العناصر الأب.
العمل مع أحداث الإدخال
أحداث الإدخال ضرورية لبناء نماذج تفاعلية. إليك مثال شامل:
import { useState } from 'react';
function SearchBar() {
const [query, setQuery] = useState('');
const [isFocused, setIsFocused] = useState(false);
const handleChange = (event) => {
setQuery(event.target.value);
};
const handleKeyPress = (event) => {
if (event.key === 'Enter') {
console.log('البحث عن:', query);
}
};
const handleClear = () => {
setQuery('');
};
const handleFocus = () => setIsFocused(true);
const handleBlur = () => setIsFocused(false);
return (
<div style={{ border: isFocused ? '2px solid blue' : '1px solid gray' }}>
<input
type="text"
value={query}
onChange={handleChange}
onKeyPress={handleKeyPress}
onFocus={handleFocus}
onBlur={handleBlur}
placeholder="بحث..."
/>
{query && (
<button onClick={handleClear}>مسح</button>
)}
<p>البحث: {query}</p>
</div>
);
}
المكونات المتحكم فيها: عندما تجمع بين الحالة وأحداث الإدخال (مثل onChange)، تنشئ مكونات متحكم فيها حيث يدير React قيمة الإدخال من خلال الحالة. هذا يمنحك السيطرة الكاملة على سلوك الإدخال.
تمرين 1: مغير الألوان
أنشئ مكوناً يغير لون خلفيته بناءً على تفاعل المستخدم:
- عرض صندوق ملون (div بلون خلفية)
- عند النقر: التغيير إلى لون عشوائي
- عند النقر المزدوج: إعادة التعيين إلى الأبيض
- عند دخول الماوس: إضافة حدود
- عند خروج الماوس: إزالة الحدود
- عرض اسم اللون الحالي أسفل الصندوق
تلميح: استخدم الحالة لتتبع اللون الحالي وحالة الحدود.
تمرين 2: مدير المهام مع اختصارات لوحة المفاتيح
ابنِ مدير مهام بسيط بدعم لوحة المفاتيح:
- حقل إدخال لإضافة مهام جديدة
- اضغط Enter لإضافة مهمة
- اضغط Escape لمسح حقل الإدخال
- عرض قائمة المهام مع أزرار الحذف
- انقر على نص المهمة لوضع علامة كمكتمل (خط عبر النص)
- عرض عدد المهام المعلقة والمكتملة
تلميح: استخدم onKeyDown للكشف عن مفاتيح Enter و Escape (event.key === 'Enter').
تمرين 3: معرض صور تفاعلي
أنشئ معرض صور بأحداث الماوس والنقر:
- عرض 6 صور مصغرة في شبكة
- عند تحريك الماوس: عرض عنوان الصورة في تلميح
- عند النقر: عرض الصورة في عرض أكبر
- إضافة أزرار "السابق" و"التالي" للتنقل
- عند النقر المزدوج: تبديل وضع ملء الشاشة
- زر إغلاق للخروج من ملء الشاشة
إضافي: أضف التنقل بلوحة المفاتيح باستخدام مفاتيح الأسهم.