معالجة الأحداث
These docs are old and won’t be updated. Go to react.dev for the new React docs.
These new documentation pages teach modern React and include live examples:
تُشبه معالجة الأحداث لعناصر React معالجة الأحداث لعناصر DOM، ولكن هنالك فروق تتعلّق بالصياغة:
- تُسمَّى أحداث React باستخدام حالة الأحرف camelCase (أي عند وجود اسم مؤلف من عدة كلمات نجعل الحرف الأول من الكلمة الأولى بالشكل الصغير أمّا باقي الكلمات نجعل حرفها الأول بالشكل الكبير) بدلًا من استخدام الشكل الصغير للأحرف.
- نُمرِّر في JSX دالة كمُعالِج للأحداث، بدلًا من سلسلة نصيّة.
على سبيل المثال لنأخذ شيفرة HTML التالية:
<button onclick="activateLasers()">
Activate Lasers
</button>
تكون الشيفرة السابقة مختلفة قليلًا في React:
<button onClick={activateLasers}> Activate Lasers
</button>
من الفروق الأخرى أنّه لا يمكنك إعادة القيمة false
لمنع السلوك الافتراضي في React، بل يجب عليك أن تستدعي preventDefault
بشكل صريح، فمثلًا في HTML لمنع السلوك الافتراضي للروابط في فتح صفحة جديدة بإمكانك كتابة ما يلي:
<form onsubmit="console.log('You clicked submit.'); return false">
<button type="submit">Submit</button>
</form>
أمّا في React فنكتب بدلًا من ذلك ما يلي:
function Form() {
function handleSubmit(e) {
e.preventDefault(); console.log('You clicked submit.');
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
يُمثِّل المتغيّر e
هنا حدثًا مُصطنعًا، حيث تُعرِّف React هذه الأحداث المُصطنعة وفق معايير W3C spec، بحيث لا نهتم بمشاكل التوافقيّة بين المتصفحات. لا تعمل أحداث React تمامًا مثل الأحداث الأصلية. للمزيد حول الأحداث المصطنعة
انتقل إلى مرجع الأحداث في React.
عند استخدام React بشكل عام لاينبغي استدعاء addEventListener
لإضافة مُستمِع للأحداث إلى عنصر DOM بعد إنشائه، وبدلًا من ذلك نُضيف مُستمِعًا للأحداث عند تصيير العنصر (Rendering Element).
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback this.handleClick = this.handleClick.bind(this); }
handleClick() { this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn })); }
render() {
return (
<button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
جرِّب هذا المثال على موقع CodePen
يجب أن تنتبه إلى معنى this
في ردود نداء JSX، ففي JavaScript لا تُربَط توابع الصّنف بشكل افتراضي عن طريق التابع bind()، وإن نسيت أن تربط وتُمرِّر this.handleClick
إلى onClick
، فستكون قيمة this
غير مُعرَّفة عند استدعاء الدالة.
لا يُعدُّ هذا سلوكًا مرتبطًا بـ React، بل هو جزء من سياق الدوال في JavaScript. بشكل عام إن أشرت إلى التابع بدون استخدام الأقواس ()
بعده، مثل onClick={this.handleClick}
، فيجب أن تربط ذلك التابع.
إن كان استدعاء التابع bind()
يزعجك، فهناك طريقتان للالتفاف حول استعماله، إن كنت تستخدم صياغة حقول الصنف العامة التجريبيّة فبإمكانك استخدام حقول الصنف لربط ردود النداء بشكل صحيح:
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick. handleClick = () => { console.log('this is:', this); }; render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
هذه الصياغة مُمكَّنة بشكل افتراضي عند استخدام الأمر Create React App.
إن لم تكن تستخدم صياغة حقول الأصناف، فبإمكانك استخدام الدوال السّهمية في ردود النداء:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick return ( <button onClick={() => this.handleClick()}> Click me
</button>
);
}
}
المشكلة في هذه الصياغة هي إنشاء رد نداء مختلف في كل مرّة يُصيَّر فيها المُكوِّن LoggingButton
، وفي معظم الحالات يكون هذا مقبولًا، ولكن إن مرَّرنا رد النداء هذا كخاصيّة prop إلى المُكوِّنات الموجودة في المستوى الأدنى، فقد تقوم هذه المُكوِّنات بعمل إعادة تصيير (re-rendering) إضافيّة. نوصي بشكل عام الربط في الدالة البانية (constructor) أو استخدام صياغة حقول الأصناف لتجنّب مثل هذا النوع من مشاكل الأداء.
تمرير وسائط إلى معالجات الأحداث
من الشائع أن نحتاج بداخل الحلقات (loops) إلى تمرير مُعامِل إضافي إلى مُعالِج الأحداث، فمثلًا إن كان المتغيّر id
يُمثِّل مُعرِّف الصف (row ID)، فسيعمل كلا السطرين التاليين بنفس الكفاءة:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
إنّ السطرين السابقين متكافئان ويستخدمان الدوال السهمية و Function.prototype.bind
على التوالي وبالترتيب.
في كلتا الحالتين سيُمرَّر الوسيط e
الذي يُمثِّل حدث React كوسيطٍ ثانٍ بعد المُعرِّف ID. في الدوال السهمية يجب أن نُمرِّره بشكلٍ صريح، ولكن في حالة استخدام التابع bind
فستُمرَّر أي وسائط أخرى تلقائيًّا.