תגובה לאירועים
React מאפשר לך להוסיף מטפלי אירועים ל-JSX שלך. מטפלי אירועים הם פונקציות משלך שיופעלו בתגובה לאינטראקציות כמו לחיצה, ריחוף, מיקוד של קלט טופס וכן הלאה.
You will learn
- דרכים שונות לכתוב מטפל באירועים
- איך להעביר לוגיקת טיפול באירועים מרכיב אב
- איך אירועים מתפשטים וכיצד לעצור אותם
הוספת מטפלי אירועים
כדי להוסיף מטפל באירועים, תחילה תגדיר פונקציה ולאחר מכן תעביר אותה כעזר לתג JSX המתאים. לדוגמה, הנה כפתור שעדיין לא עושה כלום:
export default function Button() { return ( <button> I don't do anything </button> ); }
אתה יכול לגרום לזה להציג הודעה כאשר user לוחץ על ידי ביצוע שלושת השלבים הבאים:
- הכריז על פונקציה בשם
handleClickבתוך רכיב ה-Buttonשלך. - יישם את ההיגיון בתוך אותה פונקציה (use
alertכדי להציג את ההודעה). - הוסף
onClick={handleClick}ל-<button>JSX.
export default function Button() { function handleClick() { alert('You clicked me!'); } return ( <button onClick={handleClick}> Click me </button> ); }
הגדרת את הפונקציה handleClick ולאחר מכן העברת אותה כעזר ל-<button>. handleClick הוא מטפל באירועים. פונקציות של מטפל באירועים:
- בדרך כלל מוגדרים בתוך הרכיבים שלך.
- יש שמות שמתחילים ב-
handle, ואחריו שם האירוע.
לפי המוסכמה, מקובל לתת שם למטפלי אירועים כ-handle ואחריו שם האירוע. לעתים קרובות תראה onClick={handleClick}, onMouseEnter={handleMouseEnter} וכן הלאה.
לחלופין, אתה יכול להגדיר מטפל אירועים מוטבע ב-JSX:
<button onClick={function handleClick() {
alert('You clicked me!');
}}>או, באופן תמציתי יותר, באמצעות פונקציית חץ:
<button onClick={() => {
alert('You clicked me!');
}}>כל הסגנונות הללו שווים. מטפלי אירועים מוטבעים נוחים לפונקציות קצרות.
קריאת props במטפלי אירועים
Because מטפלי אירועים מוכרזים בתוך רכיב, יש להם גישה ל-props של הרכיב. הנה כפתור שכאשר לוחצים עליו, מציג התראה עם אביזר ה-message שלו:
function AlertButton({ message, children }) { return ( <button onClick={() => alert(message)}> {children} </button> ); } export default function Toolbar() { return ( <div> <AlertButton message="Playing!"> Play Movie </AlertButton> <AlertButton message="Uploading!"> Upload Image </AlertButton> </div> ); }
זה מאפשר לשני הכפתורים האלה להציג הודעות שונות. נסה לשנות את ההודעות שהועברו אליהם.
העברת מטפלי אירועים בתור props
לעתים קרובות תרצה שרכיב האב יציין מטפל באירועים של ילד. שקול לחצנים: תלוי איפה אתה משתמש ברכיב Button, אולי תרצה לבצע פונקציה אחרת - אולי אחד מנגן סרט ואחר מעלה תמונה.
כדי לעשות זאת, העבירו אבזר שהרכיב מקבל מהאב שלו כמטפל באירועים כך:
function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); } function PlayButton({ movieName }) { function handlePlayClick() { alert(`Playing ${movieName}!`); } return ( <Button onClick={handlePlayClick}> Play "{movieName}" </Button> ); } function UploadButton() { return ( <Button onClick={() => alert('Uploading!')}> Upload Image </Button> ); } export default function Toolbar() { return ( <div> <PlayButton movieName="Kiki's Delivery Service" /> <UploadButton /> </div> ); }
כאן, הרכיב Toolbar מעבד PlayButton וUploadButton:
PlayButtonמעביר אתhandlePlayClickכמשענתonClickלButtonשבפנים.UploadButtonמעביר את() => alert('Uploading!')כמשענתonClickלButtonשבפנים.
לבסוף, רכיב Button שלך מקבל אבזר שנקרא onClick. הוא מעביר את העזר הזה ישירות לדפדפן המובנה <button> עם onClick={onClick}. זה אומר לReact לקרוא לפונקציה שעברה בלחיצה.
אם אתה use מערכת עיצוב, זה נפוץ שרכיבים כמו כפתורים מכילים סגנון אבל לא מציינים התנהגות. במקום זאת, רכיבים כמו PlayButton ו-UploadButton יעבירו מטפלים באירועים.
מתן שם לאירוע מטפל props
רכיבים מובנים כמו <button> ו-<div> תומכים רק בשמות אירועי דפדפן כמו onClick. עם זאת, כאשר אתה בונה רכיבים משלך, אתה יכול לקרוא למטפל האירועים שלהם props בכל דרך שתרצה.
לפי המוסכמה, מטפל באירועים props צריך להתחיל ב-on, ואחריו באות גדולה.
לדוגמה, הרכיב onClick של הרכיב Button יכול היה להיקרא onSmash:
function Button({ onSmash, children }) { return ( <button onClick={onSmash}> {children} </button> ); } export default function App() { return ( <div> <Button onSmash={() => alert('Playing!')}> Play Movie </Button> <Button onSmash={() => alert('Uploading!')}> Upload Image </Button> </div> ); }
בדוגמה זו, <button onClick={onSmash}> מראה שהדפדפן <button> (אותיות קטנות) עדיין זקוק לאביזר בשם onClick, אבל שם האביזר שמתקבל על ידי רכיב ה-Button המותאם אישית שלך תלוי בך!
כאשר הרכיב שלך תומך באינטראקציות מרובות, אתה יכול לקרוא למטפל באירועים props עבור מושגים ספציפיים לאפליקציה. לדוגמה, רכיב Toolbar זה מקבל מטפלי אירועים onPlayMovie וonUploadImage:
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Playing!')} onUploadImage={() => alert('Uploading!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Play Movie </Button> <Button onClick={onUploadImage}> Upload Image </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
שימו לב איך הרכיב App לא צריך לדעת מה Toolbar יעשה עם onPlayMovie או onUploadImage. זה פרט יישום של Toolbar. כאן, Toolbar מעביר אותם כמטפלי onClick ל-Buttons שלו, אבל זה יכול מאוחר יותר גם להפעיל אותם על קיצור מקשים. מתן שם ל-props לאחר אינטראקציות ספציפיות לאפליקציה כמו onPlayMovie נותן לך את הגמישות לשנות את האופן שבו הם used מאוחר יותר.
הפצת אירוע
מטפלי אירועים יתפסו גם אירועים מכל הילדים שעשויים להיות לרכיב שלך. אנו אומרים שאירוע “בוע” או “מתפשט” במעלה העץ: הוא מתחיל במקום בו התרחש האירוע, ואז עולה בעץ.
<div> זה מכיל שני כפתורים. גם <div> ו לכל כפתור יש מטפל onClick משלהם. אילו מטפלים לדעתך יירו כשתלחץ על כפתור?
export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('You clicked on the toolbar!'); }}> <button onClick={() => alert('Playing!')}> Play Movie </button> <button onClick={() => alert('Uploading!')}> Upload Image </button> </div> ); }
אם תלחץ על אחד מהלחצנים, ה-onClick שלו יפעל ראשון, ואחריו ה-onClick של האב <div>. אז יופיעו שתי הודעות. אם תלחץ על סרגל הכלים עצמו, רק ה-onClick של האב <div> יפעל.
הפסקת ההפצה
מטפלי אירועים מקבלים אובייקט אירוע כארגומנט היחיד שלהם. לפי המוסכמה, זה נקרא בדרך כלל e, אשר מייצג “אירוע”. אתה יכול use אובייקט זה כדי לקרוא מידע על האירוע.
אובייקט האירוע הזה גם מאפשר לך לעצור את ההפצה. אם ברצונך למנוע מאירוע להגיע לרכיבי אב, עליך להתקשר ל-e.stopPropagation() כמו שרכיב Button זה עושה:
function Button({ onClick, children }) { return ( <button onClick={e => { e.stopPropagation(); onClick(); }}> {children} </button> ); } export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('You clicked on the toolbar!'); }}> <Button onClick={() => alert('Playing!')}> Play Movie </Button> <Button onClick={() => alert('Uploading!')}> Upload Image </Button> </div> ); }
כאשר אתה לוחץ על כפתור:
- React קורא למטפל
onClickשהועבר ל<button>. - המטפל הזה, המוגדר ב-
Button, עושה את הפעולות הבאות:- מתקשר ל-
e.stopPropagation(), מונע מהאירוע לבעבע עוד יותר. - קורא לפונקציה
onClick, שהיא אבזר המועבר מהרכיבToolbar.
- מתקשר ל-
- פונקציה זו, המוגדרת ברכיב
Toolbar, מציגה את ההתראה של הכפתור עצמו. - מאז שההפצה הופסקה, המטפל
onClickשל האב<div>לא פועל.
כתוצאה מ-e.stopPropagation(), לחיצה על הכפתורים מציגה כעת רק התראה בודדת (מה-<button>) ולא את שניהם (מ-<button> ומסרגל הכלים של האב <div>). לחיצה על כפתור אינה זהה ללחיצה על סרגל הכלים שמסביב, לכן עצירת ההפצה הגיונית עבור ממשק המשתמש הזה.
Deep Dive
במקרים נדירים, ייתכן שיהיה עליך לתפוס את כל האירועים ברכיבי צאצא, גם אם הם הפסיקו את ההפצה. לדוגמה, אולי אתה רוצה לרשום כל קליק לניתוח, ללא קשר להיגיון ההפצה. אתה יכול לעשות זאת על ידי הוספת Capture בסוף שם האירוע:
<div onClickCapture={() => { /* this runs first */ }}>
<button onClick={e => e.stopPropagation()} />
<button onClick={e => e.stopPropagation()} />
</div>כל אירוע מתפשט בשלושה שלבים:
- זה נוסע למטה, קורא לכל המטפלים
onClickCapture. - הוא מריץ את המטפל
onClickשל הרכיב שנלחץ. - הוא נוסע כלפי מעלה, קורא לכל המטפלים
onClick.
אירועי לכידה הם use מלאים עבור קוד כמו נתבים או ניתוחים, אבל כנראה שלא use אותם בקוד האפליקציה.
העברת מטפלים כחלופה להפצה
שים לב כיצד מטפל הקליקים הזה מריץ שורת קוד ולאחר מכן קורא לאוזר onClick שהועבר על ידי האב:
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}אתה יכול להוסיף עוד קוד למטפל הזה לפני שתקרא גם למטפל onClick האב. דפוס זה מספק אלטרנטיבה להפצה. זה מאפשר לרכיב הילד לטפל באירוע, ובמקביל מאפשר לרכיב האב לציין התנהגות נוספת. בניגוד להפצה, זה לא אוטומטי. אבל היתרון של הדפוס הזה הוא שאתה יכול לעקוב בבירור אחר כל שרשרת הקוד שמתבצעת כתוצאה מאירוע כלשהו.
אם אתה מסתמך על התפשטות וקשה לאתר אילו מטפלים מבצעים ומדוע, נסה את הגישה הזו במקום זאת.
מניעת התנהגות ברירת מחדל
לאירועי דפדפן מסוימים יש התנהגות ברירת מחדל הקשורה אליהם. לדוגמה, אירוע שליחה <form>, המתרחש כאשר לוחצים על כפתור בתוכו, יטען מחדש את כל העמוד כברירת מחדל:
export default function Signup() { return ( <form onSubmit={() => alert('Submitting!')}> <input /> <button>Send</button> </form> ); }
אתה יכול להתקשר ל-e.preventDefault() באובייקט האירוע כדי למנוע את זה:
export default function Signup() { return ( <form onSubmit={e => { e.preventDefault(); alert('Submitting!'); }}> <input /> <button>Send</button> </form> ); }
אל תסובב use e.stopPropagation() וe.preventDefault(). שניהם useמלאים, אך אינם קשורים:
e.stopPropagation()עוצר את ההפעלה של מטפלי האירועים המצורפים לתגים למעלה.e.preventDefault()מונעת את התנהגות הדפדפן המוגדרת כברירת מחדל עבור כמה אירועים שיש להם את זה.
האם למטפלים באירועים יכולים להיות תופעות לוואי?
בְּהֶחלֵט! מטפלי אירועים הם המקום הטוב ביותר לתופעות לוואי.
שלא כמו פונקציות רינדור, מטפלי אירועים לא צריכים להיות טהורים, אז זה מקום מצוין לשנות משהו - לדוגמה, לשנות ערך של קלט בתגובה להקלדה, או לשנות רשימה בתגובה ללחיצת כפתור. עם זאת, כדי לשנות מידע מסוים, תחילה עליך דרך כלשהי לאחסן אותו. ב-React, זה נעשה באמצעות state, memory של רכיב. תלמד הכל על זה בעמוד הבא.
Recap
- אתה יכול לטפל באירועים על ידי העברת פונקציה כעזר לאלמנט כמו
<button>. - מטפלי אירועים חייבים לעבור, לא נקרא!
onClick={handleClick}, לאonClick={handleClick()}. - ניתן להגדיר פונקציית מטפל באירועים בנפרד או בשורה.
- מטפלי אירועים מוגדרים בתוך רכיב, כך שהם יכולים לגשת ל-props.
- ניתן להכריז על מטפל באירועים בהורה ולהעביר אותו כאביזר לילד.
- אתה יכול להגדיר מטפל אירועים משלך props עם שמות ספציפיים ליישום.
- אירועים מתפשטים כלפי מעלה. התקשר ל-
e.stopPropagation()בטיעון הראשון כדי למנוע זאת. - ייתכן שלאירועים יש התנהגות לא רצויה של דפדפן ברירת מחדל. התקשר ל-
e.preventDefault()כדי למנוע זאת. - קריאה מפורשת לאביזר של מטפל באירועים ממטפל צאצא היא חלופה טובה להפצה.
Challenge 1 of 2: תקן מטפל באירועים
לחיצה על כפתור זה אמורה להחליף את רקע העמוד בין לבן לשחור. עם זאת, שום דבר לא קורה כאשר אתה לוחץ עליו. תקן את הבעיה. (אל תדאג לגבי ההיגיון שבתוך handleClick - החלק הזה בסדר.)
export default function LightSwitch() { function handleClick() { let bodyStyle = document.body.style; if (bodyStyle.backgroundColor === 'black') { bodyStyle.backgroundColor = 'white'; } else { bodyStyle.backgroundColor = 'black'; } } return ( <button onClick={handleClick()}> Toggle the lights </button> ); }