در ری اکت، شما میتوانید مدیریتکنندههای رویداد (Event Handlers) را به JSX خود اضافه کنید. این ابزارها توابعی هستند که خودتان مینویسید و در پاسخ به تعاملات کاربر (مانند کلیک کردن، هاور شدن، فوکوس روی اینپوتها و...) اجرا میشوند.
روشهای مختلف نوشتن یک Event Handler
نحوه انتقال منطق مدیریت رویداد از کامپوننت والد به فرزند
مکانیزم انتشار رویدادها (Event Propagation) و چگونگی متوقف کردن آن
جلوگیری از رفتارهای پیشفرض مرورگر (preventDefault)
برای مدیریت یک رویداد، ابتدا باید یک تابع تعریف کنید و سپس آن را به عنوان یک prop به تگ JSX مربوطه پاس دهید.
۱. یک تابع به نام handleClick درون کامپوننت خود بسازید.
۲. منطق مورد نظر را درون تابع بنویسید (مثلاً یک alert).
۳. ویژگی onClick={handleClick} را به تگ <button> اضافه کنید.
// App.js
export default function Button() {
// ۱. تعریف تابع مدیریت رویداد
function handleClick() {
alert('شما روی دکمه کلیک کردید!');
}
return (
// ۲. پاس دادن تابع به رویداد کلیک مرورگر
<button onClick={handleClick}>
کلیک کن
</button>
);
}
معمولاً درون کامپوننتها تعریف میشوند تا به متغیرها و پراپها دسترسی داشته باشند.
نام آنها طبق قرارداد با کلمه handle شروع شده و با نام رویداد ترکیب میشود (مثل handleClick یا handleMouseEnter).
شما میتوانید به جای تعریف جداگانه، تابع را مستقیماً درون JSX بنویسید (ترجیحاً با ساختار Arrow Function):
<button onClick={() => alert('کلیک شد!')}>کلیک سریع</button>
زمانی که میخواهید تابع را به رویداد معرفی کنید، نباید جلوی آن پرانتز باز و بسته بگذارید:
| روش درست (Passing) | روش غلط (Calling) |
<button onClick={handleClick}> | <button onClick={handleClick()}> |
<button onClick={() => alert('...')}> | <button onClick={alert('...')}> |
چرا این اتفاق میافتد؟ در حالت اول (درست)، شما فقط آدرس تابع را به ریآکت میدهید تا آن را به خاطر بسپارد و فقط زمانی که کاربر واقعاً کلیک کرد، اجرایش کند.
در حالت دوم (غلط)، وجود پرانتز () باعث میشود تابع دقیقاً در لحظه رندر شدن صفحه (بدون هیچ کلیکی) فوراً اجرا شود!
از آنجا که این توابع درون بدنه کامپوننت تعریف میشوند، به تمام پراپهای کامپوننت دسترسی کامل دارند:
function AlertButton({ message, children }) {
return (
// دسترسی مستقیم به پراپ message
<button onClick={() => alert(message)}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div>
<AlertButton message="در حال پخش فیلم...">پخش فیلم</AlertButton>
<AlertButton message="در حال آپلود عکس...">آپلود عکس</AlertButton>
</div>
);
}
در سیستمهای طراحی (Design Systems)، کامپوننتهایی مثل دکمهها فقط حاوی استایل هستند و رفتار آنها را کامپوننت والد مشخص میکند. برای این کار، میتوانید تابع را از والد به فرزند پاس دهید:
// فرزند پراپ onClick را میگیرد و به دکمه اصلی مرورگر متصل میکند
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div>
<Button onClick={() => alert('فیلم پخش شد!')}>پخش فیلم</Button>
<Button onClick={() => alert('عکس آپلود شد!')}>آپلود عکس</Button>
</div>
);
}
تگهای استاندارد مرورگر (مثل button با حروف کوچک) فقط رویدادهای رسمی مثل onClick را میفهمند. اما وقتی خودتان یک کامپوننت میسازید، نام پراپِ رویداد کاملاً دست خودتان است! طبق قرارداد، بهتر است نام آن با on شروع شده و حرف بعدی بزرگ باشد:
// استفاده از یک نام سفارشی مثل onSmash
function Button({ onSmash, children }) {
return (
<button onClick={onSmash}> {/* تگ بومی مرورگر همچنان onClick میخواهد */}
{children}
</button>
);
}
رویدادها در ریآکت از جایی که اتفاق افتادهاند شروع شده و لایه به لایه در درخت کامپوننتها به سمت بالا حرکت میکنند (اصطلاحاً حبابسازی یا Bubbling رخ میدهد).
فرض کنید ساختار زیر را دارید:
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => alert('شما روی نوار ابزار کلیک کردید!')}>
<button onClick={() => alert('در حال پخش فیلم...')}>پخش فیلم</button>
</div>
);
}
اگر روی دکمه کلیک کنید:
۱. ابتدا رویداد خود دکمه اجرا میشود (alert('در حال پخش فیلم...')).
۲. رویداد به سمت بالا منتشر شده و رویداد div والد نیز اجرا میشود (alert('شما روی نوار ابزار کلیک کردید!')).
💡 نکته: تمام رویدادها در ریآکت به سمت بالا منتشر میشوند، به جز رویداد
onScrollکه فقط روی همان تگی که به آن متصل شده عمل میکند.
e.stopPropagation)تمام توابع مدیریت رویداد، یک شیء به عنوان آرگومان دریافت میکنند که به صورت قراردادی e (مخفف Event) نامیده میشود. این شیء حاوی اطلاعات رویداد است و به شما اجازه میدهد جلوی حرکت رویداد به سمت بالا را بگیرید.
با صدا زدن متد e.stopPropagation()، رویداد در همان لایه قرنطینه شده و لایههای بالاتر باخبر نمیشوند:
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation(); // 🔴 سد کردن راه انتشار رویداد به لایههای بالاتر
onClick();
}}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div onClick={() => alert('این دایو والد دیگر اجرا نمیشود!')}>
<Button onClick={() => alert('فقط همین پیام نشان داده میشود.')}>
کلیک کن
</Button>
</div>
);
}
e.preventDefault)برخی رویدادها در مرورگر یک رفتار سنتی دارند؛ مثلاً وقتی روی دکمه داخل یک تگ <form> کلیک میکنید، مرورگر تلاش میکند کل صفحه را رفرش و فرم را ارسال کند. برای جلوگیری از این رفتار، از متد e.preventDefault() استفاده میشود:
export default function Signup() {
return (
<form onSubmit={e => {
e.preventDefault(); // 🛑 جلوگیری از رفرش شدن صفحه توسط مرورگر
alert('فرم با موفقیت ثبت شد!');
}}>
<input />
<button>ارسال فرم</button>
</form>
);
}
e.stopPropagation(): جلوی اجرای رویدادهای تگهای والد (بالاسری) را میگیرد.
e.preventDefault(): جلوی رفتار پیشفرض بومی مرورگر برای آن رویداد خاص را میگیرد.
در موارد بسیار نادر، ممکن است بخواهید تمام رویدادهای فرزندان را بگیرید، حتی اگر آنها انتشار رویداد را با stopPropagation قطع کرده باشند (مثلاً برای سیستمهای آنالیز دیتای کاربر یا لاگگیری). برای این کار کافیست کلمه Capture را به آخر نام رویداد والد اضافه کنید:
<div onClickCapture={() => { /* این تابع قطعاً اول از همه اجرا میشود */ }}>
<button onClick={e => e.stopPropagation()} />
</div>
هر رویداد در سه فاز حرکت میکند:
۱. حرکت به سمت پایین (اجرای تمام لایههای دارای onClickCapture).
۲. اجرای رویدادِ خود المان کلیک شده (onClick).
۳. حرکت به سمت بالا (اجرای تمام لایههای دارای onClick معمولی).
رویدادها را با پاس دادن آدرس تابع مدیریت کنید: onClick={handleClick} (بدون پرانتز).
برای دکمهها و عناصر تعاملی همیشه از تگهای بومی (مثل <button>) استفاده کنید تا قابلیتهای دسترسی (Accessibility) و کیبورد مروگر حفظ شوند.
توابع مدیریت رویداد بهترین مکان برای ایجاد تغییرات و عوارض جانبی (Side Effects) مثل تغییر وضعیتها (State) هستند و نیازی نیست توابع خالصی (Pure) باشند.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://react.dev/learn