Eventها این امکان را به توسعهدهندگان میدهند تا با مدیریت درست آنها، به تعامل کاربران در برنامههای خود به سرعت و به صورت کارآمد پاسخ دهند. در این مقاله قصد داریم تا به بررسی عمیق event handlerهای onClick در React بپردازیم، بیشتر با آنها آشنا شویم و بدانیم که چگونه و چه زمانی باید از این event handlerها استفاده کنیم.
Event Handlerها تعیین میکنند که هر زمان که یک event یا رویدادی اجرا میشود، چه اکشنی باید صورت بگیرد. این رویداد میتواند کلیک بر روی یک دکمه یا تغییر در متن یک input باشد.
در اصل، event handlerها این امکان را برای کاربران فراهم میکنند تا بتوانند با برنامه React تعامل داشته باشند. مدیریت رویدادها با المنتهای React مشابه مدیریت رویدادها در المنتهای DOM است، البته چند تفاوت جزئی نیز وجود دارد.
onClick یک مفهوم اساسی در توسعه وب است، زیرا به کاربران اجازه میدهد تا با المنتهای موجود در صفحه تعامل داشته باشند. همانطور که میتوانیم در React از آن استفاده کنیم، در HTML و جاوااسکریپت هم میتوانیم از آن بهرهمند شویم.
اگرچه تفاوتهایی در سینتکس وجود دارد، اما مفهوم اصلی event handlerهای click در React، HTML و جاوااسکریپت ثابت است. در هر سه رویکرد، زمانی که کاربر بر روی یک المنت تعیینشده کلیک میکند، یک تابع یا action فعال میشود.
درک این مشترکات انتقال بین HTML، جاوااسکریپت و React را آسانتر میکند و توسعهدهندگان را قادر میسازد تا از دانش خود در زمینههای مختلف استفاده نمایند.
در ادامه قصد داریم تا شباهتها و تفاوتهای استفاده از رویداد onClick
را در این سه رویکرد بررسی کنیم.
در HTML، از ویژگی onClick برای مرتبط کردن یک اسکریپت یا action با یک event کلیک روی یک المنت استفاده میکنیم. این روش یک راه اساسی و مستقیم برای مدیریت تعاملات کاربر به حساب میآید:
<button onclick="handleClick()">Click me</button>
در این مثال، تابع handleClick
با کلیک روی دکمه اجرا میشود. این رویکرد ساده است و معمولاً در داکیومنتهای HTML مورد استفاده قرار میگیرد.
در جاوااسکریپت، event listenerها برای مدیریت eventهای کلیک به کار میروند. متد addEventListener
به توسعهدهندگان اجازه میدهد تا توابع را به رویدادهای خاص، از جمله کلیکها، متصل کنند:
const myElement = document.getElementById('myElement'); myElement.addEventListener('click', function() { // Handle the click event here });
این روش انعطافپذیری بیشتری را در مقایسه با ویژگیهای inline مربوط به HTML فراهم میکند.
در React، رویداد onClick بخش اصلی مدیریت تعاملات کاربر است. در JSX، ما مستقیماً onClick را در کامپوننت تعریف میکنیم و این موضوع باعث میشود که کدی که داریم واضحتر شود:
import React from 'react'; const MyComponent = () => { const sayHello = () => { alert("Hello!") }; return ( <button onClick={sayHello}>Say Hello</button> ); };
handler onClick در React از مفهومی مشابه با ویژگی onClick در HTML پیروی میکند، اما بیشتر در ساختار کامپوننت ادغام شده است.
onClick event handler در React به ما این امکان را میدهد تا زمانی که کاربر روی المنتی مانند دکمه کلیک میکند، یک تابع را فراخوانی کرده و یک action را راهاندازی نماید.
نام eventها به صورت camelCase نوشته میشود، بنابراین event onclick
در یک برنامه React باید به صورت onClick
نوشته شود. علاوه بر این، event handlerهای React در داخل {}
قرار میگیرند.
مثال ساده زیر را که در HTML نوشته شده است را در نظر میگیریم:
<button onclick="sayHello()"> Say Hello <button>
در یک برنامه ریاکت، event button onClick
به صورت زیر نوشته میشود:
<button onClick={sayHello}> Say Hello <button>
یکی دیگر از تفاوتهای کلیدی این است که ما در React باید به طور صریح preventDefault
را فراخوانی کنیم، در حالی که در HTML، برای جلوگیری از رفتار پیشفرض، به سادگی false
را return مینماییم.
مثال زیر نشان میدهد که چگونه میتوانیم در HTML به طور پیشفرض از باز شدن یک لینک در یک صفحه جدید جلوگیری کنیم:
<a href="#" onclick="console.log('The link was clicked.'); return false"> Click me </a>
این کار را در React میتوانیم به صورت زیر انجام دهیم:
function ActionLink() { const handleClick = (e) => { e.preventDefault(); console.log('The link was clicked.'); } return ( <button onClick={handleClick}> Click me </button> ); }
اکنون قصد داریم تا یک مثال پیچیدهتر از استفاده از handler onClick
را بررسی کنیم. فرض کنید فرمی با چندین فیلد ورودی داریم و میخواهیم مطمئن شویم که کاربر قبل از ارسال فرم، دادههای معتبری را در فیلدها وارد کرده است:
import React, { useState } from "react"; const FormWithValidation = () => { const [formData, setFormData] = useState({ firstName: "", lastName: "", email: "", }); const [formErrors, setFormErrors] = useState({ firstName: "", lastName: "", email: "", }); const handleInputChange = (event) => { const { name, value } = event.target; // Update form data setFormData({ ...formData, [name]: value, }); // Perform validation if (name === "firstName" && value === "") { setFormErrors({ ...formErrors, firstName: "First name is required.", }); } else if (name === "lastName" && value === "") { setFormErrors({ ...formErrors, lastName: "Last name is required.", }); } else if (name === "email" && !/^\S+@\S+\.\S+$/.test(value)) { setFormErrors({ ...formErrors, email: "Invalid email address.", }); } else { // Clear validation errors if input is valid setFormErrors({ ...formErrors, [name]: "", }); } }; const handleSubmit = (event) => { event.preventDefault(); // Perform validation before submitting the form const validationErrors = Object.keys(formData).reduce((errors, name) => { if (formData[name] === "") { errors[name] = `${ name.charAt(0).toUpperCase() + name.slice(1) } is required.`; } else if (name === "email" && !/^\S+@\S+\.\S+$/.test(formData[name])) { errors[name] = "Invalid email address."; } return errors; }, {}); // Update form errors setFormErrors(validationErrors); // Check if there are any validation errors if (Object.values(validationErrors).every((error) => error === "")) { // Perform custom business logic or submit the form console.log("Form submitted successfully!"); console.log("Form Data:", formData); } else { console.log("Form validation failed. Please check the errors."); } }; return ( <form> <label> First Name: <input type="text" name="firstName" value={formData.firstName} onChange={handleInputChange} /> <span className="error">{formErrors.firstName}</span> </label> <label> Last Name: <input type="text" name="lastName" value={formData.lastName} onChange={handleInputChange} /> <span className="error">{formErrors.lastName}</span> </label> <label> Email: <input type="email" name="email" value={formData.email} onChange={handleInputChange} /> <span className="error">{formErrors.email}</span> </label> <button type="submit" onClick={handleSubmit}>Submit</button> </form> ); }; export default FormWithValidation;
در این مثال، state formErrors
را برای پیگیری خطاهای اعتبارسنجی برای هر فیلد ورودی تعریف میکنیم.
به این ترتیب، تابع handleInputChange
مسئول بهروزرسانی دادههای فرم، انجام اعتبارسنجی و بهروزرسانی state خطا بر این اساس است. همچنین، تابع handleSubmit
قبل از ارسال فرم یا اجرای منطق سفارشی، بررسی میکند که آیا خطاهای اعتبارسنجی وجود دارد یا خیر.
این فرم پیشرفته نشان میدهد که ما چگونه میتوانیم منطق اعتبارسنجی را با استفاده از onClick در کامپوننتهای React خود بگنجانیم و اطمینان حاصل کنیم که ورودیهای کاربر قبل از ارسال، معیارهای مشخصشده را برآورده میکنند.
React سیستم eventهای synthetic (مصنوعی) را پیادهسازی میکند که سازگاری و عملکرد بالا را برای برنامهها و رابطهای React به ارمغان میآورد. این سیستم با عادیسازی رویدادها به یکپارچگی دست مییابد تا ویژگیهای یکسانی در مرورگرها و پلتفرمهای مختلف داشته باشند.
synthetic event یک پوشش cross-browser در اطراف event اصلی مرورگر است که رابط کاربری مشابه event اصلی مرورگر، از جمله stopPropagation()
و preventDefault()
دارد، و تفاوتی که وجود دارد این است که eventها در همه مرورگرها یکسان عمل میکنند.
بنابراین، eventهای synthetic با استفاده از event delegation به طور خودکار به عملکرد بالایی دست پیدا میکنند. در واقع، React کنترلکنندههای رویداد را به خود nodeها متصل نمیکند. در عوض، یک event listener به root داکیومنت متصل میشود. هنگامی که یک event اجرا میشود، React آن را به المنت کامپوننت مناسب نگاشت میکند.
برای گوش دادن به رویدادها در React، ویژگی onClick را که یک event handler است، به المنت مورد نظر خود اضافه میکنیم. همانطور که در مثال زیر میبینیم، تابعی را که باید هنگام کلیک روی آن المنت اجرا شود، مشخص میکند:
import React from "react"; const ShowAlertComponent = () => { const showAlert = () => { alert("I'm an alert"); } return <button onClick={showAlert}>Show alert</button>; } export default ShowAlertComponent;
در مثال بالا، ویژگی onClick روی تابع showAlert
به عنوان هدف event تنظیم شده است که با کلیک روی دکمه، پیام هشدار I'm an alert
را نشان میدهد.
راههای مختلفی برای مدیریت eventها در کامپوننتهای فانکشنال React وجود دارد. ما در این بخش به بررسی پنج مورد از آنها خواهیم پرداخت.
توابع inline به ما این امکان را میدهند تا به طور مستقیم کدی را برای مدیریت event در JSX بنویسیم. برای مثال:
import React from "react"; const App = () => { return ( <button onClick={() => alert("Hello!")}>Say Hello</button> ); }; export default App;
این روش معمولا برای جلوگیری از تعریف تابع اضافی در خارج از JSX استفاده میشود. با این حال، اگر محتوای تابع inline بیش از حد باشد، میتواند خوانایی کمتری داشته باشد که نگهداری کد را سختتر میکند.
فرض کنید یک برنامه React داریم که از ما میخواهد تا state محلی را در یک onClick event handler بهروزرسانی کنیم. برای انجام این کار به صورت زیر عمل میکنیم:
import React, { useState } from "react"; const App = () => { const [count, setCount] = useState(0); return ( <div> <p>{count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <button onClick={() => setCount(count - 1)}>Decrement</button> </div> ); }; export default App;
در مثال بالا، مقدار useState
توسط دکمههای Increment
و Decrement
تغییر میکند که تابع بهروزرسانی setCount
آن در event handler onClick
قرار دارد.
event handler onClick
همچنین امکان فراخوانی چندین تابع را به ما میدهد. به این صورت که:
import React, { useState } from "react"; const App = () => { const [count, setCount] = useState(0); const sayHello = () => { alert("Hello!"); }; return ( <div> <p>{count}</p> <button onClick={() => { sayHello(); setCount(count + 1); }} > Say Hello and Increment </button> </div> ); }; export default App;
در بلاک کد بالا، با هر بار کلیک روی دکمه، state محلی افزایش پیدا میکند و یک پیام در alert نمایش داده میشود. هر دو اکشن توسط توابع جداگانه در event handler onClick
اجرا میشوند.
یکی دیگر از موارد استفاده متداول برای event handler، ارسال یک پارامتر به یک تابع است تا بتوانیم بعداً از آن استفاده کنیم. مثلا:
import React from "react"; const App = () => { const sayHello = (name) => { alert(`Hello, ${name}!`); }; return ( <button onClick={() => { sayHello("Yomi"); }} > Say Hello </button> ); }; export default App;
در این مثال، تابع sayHello
یک نام را به عنوان پارامتر میپذیرد، که در ادامه برای سفارشی کردن پیام هشدار از آن استفاده میکند. به این ترتیب در این مثالی که داریم، تابع نام Yomi
را میپذیرد و با کلیک روی دکمه به عنوان پیام Hello, Yomi!
را نمایش میدهد.
ما همچنین میتوانیم از eventهای synthetic مستقیماً در onClick event handler استفاده کنیم. در مثال زیر، value دکمه را از طریق e.target.value
بازیابی میکنیم و سپس از آن برای نمایش یک پیام هشدار استفاده مینماییم:
import React from "react"; const App = () => { return ( <button value="Hello!" onClick={(e) => alert(e.target.value)}> Say Hello </button> ); }; export default App;
Event delegation یک تکنیک قدرتمند در React است که به ما این امکان را میدهد تا eventها را، به خصوص در برنامههایی که تعداد المنتهای داینامیک زیادی دارند به طور موثر مدیریت کنیم. به جای اینکه event listenerها را به هر المنت متصل کنیم، میتوانیم مدیریت eventها را به یک ancestor (جد) مشترک واگذار نماییم.
این رویکرد نه تنها عملکرد را بهینه میکند، بلکه مدیریت event را نیز سادهتر میکند، به خصوص در سناریوهایی که المنتها به صورت داینامیک اضافه یا حذف میشوند.
مثال زیر را در نظر میگیریم که در آن لیستی از آیتمها به صورت داینامیک رندر میشود و ما قصد داریم تا یک event کلیک را روی هر آیتمی که در فهرست وجود دارد، مدیریت نماییم:
import React from "react"; const ItemList = () => { const handleClick = (event) => { const itemId = event.target.dataset.itemId; console.log(`Clicked item with ID: ${itemId}`); }; const items = [ { id: 1, text: "Item 1" }, { id: 2, text: "Item 2" }, { id: 3, text: "Item 3" }, ]; return ( <ul onClick={handleClick}> {items.map((item) => ( <li key={item.id}>{item.text}</li> ))} </ul> ); }; export default ItemList;
در این مثال، یک event listener که قابلیت یکبار کلیک را دارد به المنت <ul>
، که جد مشترک همه آیتمهای لیست میباشد، متصل شده است.
Event delegation مخصوصاً زمانی مفید است که با محتوای تولید شده به صورت داینامیک سروکار داریم یا زمانی که میخواهیم از افزودن چندین listener به المنتهای تکی خودداری کنیم. این روش به ما کمک میکند تا یک برنامه React کارآمدتر و مقیاسپذیر داشته باشیم.
وقتی صحبت از eventها در React به میان میآید، فقط المنتهای DOM مجاز به داشتن event listener هستند. مثالی از کامپوننتی به نام CustomButton
با event onClick
را در نظر میگیریم. این دکمه به دلیلی که به آن اشاره کردیم، به کلیکها پاسخ نمیدهد.
سوالی که باید به آن پاسخ دهیم این است که چگونه باید مدیریت event را برای کامپوننتهای سفارشی در React انجام دهیم؟ ما میتوانیم این کار را با رندر کردن یک المنت DOM در داخل کامپوننت CustomButton
و ارسال prop onClick
به آن انجام دهیم.
import React from "react"; const CustomButton = ({ onPress }) => { return ( <button type="button" onClick={onPress}> Click on me </button> ); }; const App = () => { const handleEvent = () => { alert("I was clicked"); }; return <CustomButton onPress={handleEvent} />; }; export default App;
در مثال بالا، کامپوننت CustomButton
یک prop به نام onPress
ارسال میکند، که درنهایت به onClick button
منتقل میشود.
تایپ استاتیک تایپ اسکریپت با بررسی مشکلات احتمالی در طول زمان کامپایل، یک لایه اطمینان اضافی را برای توسعه React به ارمغان میآورد. با event handlerها، میتوانیم تایپهای دقیقی را تعریف کنیم و از یک پایگاه کد قوی اطمینان حاصل نماییم.
در مثال پایین قصد داریم تا کامپوننت ItemList
خود را به یک کامپوننت تایپ اسکریپت تغییر دهیم:
import React, { MouseEvent } from "react"; interface ListItem { id: number; text: string; } const ItemList = () => { const handleClick = (event: MouseEvent<HTMLUListElement>) => { const target = event.target as HTMLLIElement; const itemId = target.dataset.itemId; console.log(`Clicked item with ID: ${itemId}`); }; const items: ListItem[] = [ { id: 1, text: "Item 1" }, { id: 2, text: "Item 2" }, { id: 3, text: "Item 3" }, ]; return ( <ul onClick={handleClick}> {items.map((item) => ( <li key={item.id}>{item.text}</li> ))} </ul> ); }; export default ItemList;
در این مثال، type annotation MouseEvent<HTMLUListElement>
تایپ اسکریپت تضمین میکند که پارامتر event
در handleClick
از نوع صحیح است. این type safety به دسترسی به ویژگیهایی مانند dataset.itemId
در المنت هدف هم گسترش پیدا میکند.
با استفاده از تایپ اسکریپت برای بررسی تایپ، میتوانیم مشکلات احتمالی مربوط به مدیریت event در زمان کامپایل را پیدا کنیم. این کار باعث میشود تا یک برنامه React قابل نگهداری و قدرتمند داشته باشیم. این رویکرد خوانایی کد را افزایش داده و احتمال خطاهای زمان اجرا را کاهش میدهد و به تجربه توسعه روانتر کمک میکند.
به طور کلی مدیریت event در React، برای ایجاد رابطهای کاربری تعاملی بسیار مهم است. هنگامی که صحبت از مدیریت eventها میشود، تایپ اسکریپت با معرفی تایپ استاتیک، تجربه توسعه را بهبود میبخشد و به توسعهدهندگان این امکان را میدهد تا مشکلات احتمالی را در طول زمان کامپایل پیدا کنند.
با این حال، React از سیستم event مخصوص به خود استفاده میکند، به این معنی که نمیتوانیم مستقیماً از eventها استاندارد DOM استفاده نماییم. در عوض، تایپ اسکریپت مجموعهای از تایپهای eventهای از پیش تعریف شده را ارائه میدهد که به طور خاص برای React طراحی شدهاند.
این تایپها مشابه DOM خود را منعکس میکنند، اما برای ادغام یکپارچه با کامپوننتهای React طراحی شدهاند. برای eventهای onClick در React، تایپ event handler MouseEvent
است.
mouse event نوعی تعامل کاربر است و زمانی رخ میدهد که کاربر با یک صفحه وب از طریق ماوس تعامل داشته باشد. در توسعه وب، رویدادهای ماوس شامل اقداماتی مانند کلیک کردن، hover کردن، حرکت دادن و غیره دکمههای ماوس است.
MouseEvent
یک تایپ عمومی mouse event است، در حالی که MouseEvent<HTMLButtonElement>
یک mouse event مخصوص یک المنت <button>
میباشد. به عنوان مثال:
import React, { MouseEvent } from "react"; const handleClick = (event: MouseEvent<HTMLButtonElement>) => { // Handle the click event for the button console.log(event.clientX, event.clientY); }; const MyButton = () => { return ( <button onClick={handleClick}> Click me </button> ); };
انواع eventهای از پیش تعریف شده اضافی در تایپ اسکریپت برای eventهای مختلف React، مانند ChangeEvent
، KeyboardEvent
و غیره وجود دارد. با این حال، تایپ MouseEvent
یک event type است که در درجه اول با event onClick
استفاده میشود.
با استفاده از تایپ استاتیک تایپ اسکریپت، توسعهدهندگان میتوانند دقیقاً ساختار event و ویژگیهای مورد انتظار خود را برای handler onClick
تعریف کنند و از یک پایگاه کد قویتر و مقاومتر در برابر خطا اطمینان حاصل نمایند.
در حالی که رویداد onClick در React یک جنبه اساسی در مدیریت تعاملات کاربر بهشمار میآید، توسعهدهندگان اغلب با مشکلات رایجی مواجه میشوند که میتواند منجر به خطاها و رفتارهای غیرمنتظره در برنامه شود. در ادامه برخی از این مشکلات را بررسی کرده و راه حلهایی را برای آنها ارائه خواهیم کرد:
هنگامی که المنتهای تودرتو با handlerهای onClick مخصوص به خود داشته باشیم، این eventها ممکن است bubble up شوند و بهطور ناخواسته باعث فعال شدن چندین handler گردند.
به عنوان یک راه حل، میتوانیم از متد stopPropagation
برای جلوگیری از انتشار event به المنتهای parent استفاده کنیم:
const handleClick = (event) => { event.stopPropagation(); // Your click handling logic };
this
اگر this
در کلاس کامپوننتها یا هنگام استفاده از توابع به درستی محدود نشده باشد، میتواند منجر به ایجاد خطاهای متد undefined
شود. ما میتوانیم با استفاده از arrow functionها یا تعریف صریح this
در کلاس کامپوننتها، اتصال صحیح را تضمین نماییم:
// Using arrow function in class component class MyComponent extends React.Component { handleClick = () => { // Your click handling logic }; } // Explicitly binding `this` in class component class MyComponent extends React.Component { constructor() { super(); this.handleClick = this.handleClick.bind(this); } handleClick() { // Your click handling logic } }
بهروزرسانی state به صورت asynchronous در handler onClick
ممکن است منجر به ایجاد مقادیر غیرمنتظره برای state شود. برای حل این مشکل، زمانی که state جدید به state قبلی بستگی دارد، میتوانیم از فانکشنال فرم setState
استفاده کنیم:
const handleClick = () => { // Async state update // Incorrect way: // setState({ count: count + 1 }); // Correct way: setState((prevState) => ({ count: prevState.count + 1 })); };
cleanup نکردن event listenerها یا اشتراک در کامپوننتها میتواند منجر به ایجاد memory leak شود. برای از بین بردن اشتراک یا حذف event listenerها از مکانیسمهای cleanup مانند useEffect
یا componentWillUnmount
در کلاس کامپوننتها استفاده نماییم:
useEffect(() => { const handleClick = () => { // Your click handling logic }; document.addEventListener('click', handleClick); return () => { // Cleanup: Remove the event listener document.removeEventListener('click', handleClick); }; }, []);
داشتن عملیات پیچیده یا غیرضروری در یک handler onClick
میتواند بر روی عملکرد تأثیر منفی بگذارد. ما میتوانیم مشکلات عملکرد را با بهینهسازی handlerهای کلیک کاهش دهیم. به عنوان مثال:
const handleClick = () => { // Optimize and perform necessary operations }; // Debouncing example with lodash import { debounce } from 'lodash'; const debouncedClickHandler = debounce(handleClick, 300); <button onClick={debouncedClickHandler}>Click me</button>
پرداختن به این مشکلات رایج تجربه مدیریت onClick را در برنامههای React که داریم تضمین میکند. با درک این چالشها و به کارگیری راه حلهای مناسب، توسعهدهندگان میتوانند قابلیت اطمینان و عملکرد رابط کاربری خود را افزایش دهند.
Event handlerها تعیین میکنند که در هنگام وقوع یک رویداد چه اقدامی باید انجام شود. event onClick
برای گوش دادن به رویدادهای کلیک بر روی المنتهای DOM استفاده میشود.
ما در این مقاله سعی کردیم تا برخی از متداولترین موارد استفاده از onClick event handler را در فانکشنال کامپوننتها، مانند بهروزرسانی state، فراخوانی چندین تابع و استفاده از رویدادهای synthetic مرور کنیم.
همچنین نحوه عملکرد onClick event handler در کامپوننتهای سفارشی را بررسی کردیم و به برخی از مشکلات رایجی که توسعهدهندگان ممکن است با آنها مواجه شوند، مانند event bubbling، مشکلات binding this
، بهروزرسانیهای state به صورت asynchronous، مشکلات memory leak و مشکلات عملکرد پرداختیم و راهحلهایی برای آنها ارائه نمودیم.
در نهایت، روش تعیین و افزودن تایپهای event handler با تایپ اسکریپت به event handlerها را بررسی کردیم.