טיפול באירועים

טיפול באירועים עם אלמנטים של React דומה מאוד לטיפול באירועים באלמנטים של DOM. ישנם כמה הבדלים תחביריים:

  • שמות אירועים של React נכתבים באמצעות תחביר camelCase (כל תחילת מילה באות גדולה פרט לראשונה), ולא באותיות קטנות.
  • ב-JSX מעבירים פונקציה כמטפל האירוע, ולא מחרוזת.

למשל, ה-HTML:

<button onclick="activateLasers()">
  הפעל לייזרים
</button>

הוא מעט שונה ב-React:

<button onClick={activateLasers}>
  הפעל לייזרים
</button>

הבדל נוסף הוא שאינכם יכולים להחזיר false כדי למנוע התנהגות ברירת מחדל ב-React. אתם חייבים לקרוא ל-preventDefault במפורש. לדוגמה, עם HTML רגיל, כדי למנוע את התנהגות ברירת המחדל עבור קישור של פתיחת דף חדש, אתם יכולים לכתוב:

<a href="#" onclick="console.log('הקישור נלחץ.'); return false">
  לחץ עלי
</a>

ב-React, זה יכול להיות במקום זאת:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

כאן, e הוא אירוע סינתטי. React מגדיר אירועים סינתטיים אלה בהתאם למפרט W3C, כך שאתם לא צריכים לדאוג לתאימות בין דפדפנים. עיינו בהפנייה למדריך SyntheticEvent כדי ללמוד עוד.

בעת שימוש ב-React אתם בדרך כלל לא צריכים לקרוא ל-addEventListener כדי להוסיף מאזינים לאלמנט DOM לאחר שנוצר. במקום זאת, רק ספקו מאזין כאשר האלמנט רונדר בהתחלה.

כאשר אתם מגדירים קומפוננטה באמצעות מחלקת ES6, זהו דפוס נפוץ שמטפל אירוע הוא מתודה במחלקה. למשל, רכיב Toggle זה מרנדר כפתור המאפשר למשתמש לעבור בין מצבי “ON” ו-”OFF”:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // ה-binding הזה הכרחי כדי לגרום לכך ש-`this` יעבוד בתוך ה-callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      isToggleOn: !state.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

נסו זאת ב-CodePen

עליכם להיות זהירים לגבי המשמעות של this בקריאות JSX. ב-JavaScript, מתודות מחלקה אינן bound כברירת מחדל. אם תשכחו לעשות bind ל-this.handleClick ותעבירו אותה ל-onClick, this יהיה undefined כאשר הפונקציה תקרא למעשה.

זו אינה התנהגות ספציפית ל-React; זה חלק מאיך שפונקציות פועלות ב-JavaScript. באופן כללי, אם אתם מתייחסים למתודה ללא () אחריה, כגון onClick={this.handleClick}, עליכם לעשות bind לאותה מתודה.

אם קריאה ל-bind מפריעה לכם, יש שתי דרכים לעקוף את זה. אם אתם משתמשים בתחביר שדות ציבוריים של מחלקה הנסיוני, תוכלו להשתמש בשדות המחלקה כדי לעשות bind ל-callbacks בדרך הנכונה:

class LoggingButton extends React.Component {
  // תחביר זה מבטיח ש-`this` הוא bound בתוך handleClick.
  // אזהרה: זהו תחביר *ניסיוני*.
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

תחביר זה מופעל כברירת מחדל ב-Create React App. אם אינכם משתמשים בתחביר שדות של מחלקה, באפשרותכם להשתמש בפונקצית חץ ב-callback:

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // תחביר זה מבטיח ש-`this` הוא bound בתוך handleClick
    return (
      <button onClick={(e) => this.handleClick(e)}>
        Click me
      </button>
    );
  }
}

הבעיה עם תחביר זה היא שנוצר callback שונה בכל פעם שה-LoggingButton מרונדר. ברוב המקרים, זה בסדר. עם זאת, אם callback זה מועבר כ-prop לקומפוננטות נמוכות יותר, קומפוננטות אלו עשויות לבצע רינדור מחדש נוסף. באופן כללי אנו ממליצים על ביצוע binding בבנאי או באמצעות תחביר שדות מחלקה, כדי למנוע בעית ביצועים זו.

העברת ארגומנטים למטפלי אירועים

בתוך לולאה זהו דבר נפוץ לרצות להעביר פרמטר נוסף למטפל האירוע. לדוגמה, אם id הוא מזהה השורה, כל אחת מהאפשרויות הבאות תעבוד:

<button onClick={(e) => this.deleteRow(id, e)}>מחק שורה</button>
<button onClick={this.deleteRow.bind(this, id)}>מחק שורה</button>

שתי השורות למעלה שוות, ומשתמשות בפונקציות חץ ו-Function.prototype.bind בהתאמה.

בשני המקרים, הארגומנט e שמייצג את אירוע ה-React יועבר כארגומנט שני לאחר המזהה. עם פונקציית חץ, אנחנו צריכים להעביר אותו במפורש, אבל עם bind כל הארגומנטים הנוספים מועברים באופן אוטומטי.