קומפוננטת ריאקט עם אירועים

איך מנהלים events בקומפוננטות ריאקט

במאמר הקודם למדנו על state ועל איך אנחנו משתמשים בו על מנת לאחסן נתונים בקומפוננטה. אבל במאמר הקודם הראינו איך אנחנו מפעילים מתודה כל שניה באופן אוטומטי. במציאות אנחנו נרצה לשנות את ה-state באמצעות פעולות של הגולש. למשל קליקים שיעשו משהו – ישנו את ה state או יקראו לאיזו פונקציה אחרת שתגרום לשינוי של ה state. כך או אחרת – אנחנו צריכים אירועים – כלומר תגובות לדברים שהגולש עושה בקומפוננטות שלנו.

איך נדגים? תסתכלו על אפליקצית הריאקט הזו. היא סופרת קליקים. יש פה שתי קומפוננטות: ClickCounterDisplay שאחראית על התצוגה ועל ה-CSS ובתוכה, הס-פן-תעיר ישנה לה קומפוננטה זעירה בשם ClickCounter. כרגע מה שיש בה זה איתחול של state עם clicksNumber למספר 0.

כך יראה ה-HTML המלא:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>My first react app and component</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/[email protected]/babel.min.js"></script>
</head>

<body>
<div id="content"></div>
<script type="text/babel">
class ClickCounterDisplay extends React.Component {
  render() {
    const bigStyle = {
      backgroundColor: 'black',
      borderRadius: 10,
      color: 'silver',
      fontSize: '42px',
      textAlign: 'center',
      width: 250,
    };
    const smallStyle = {
      color: 'gold',
      fontSize: '25px',
    };
    return <div style={bigStyle}>
        <ClickCounter />
        <div style={smallStyle}>Clicks</div>
      </div>
  }
}

class ClickCounter extends React.Component {
  constructor(props) {
    super(props);     
    this.state = {
      clicksNumber: 0,
    };
  }
  
  render() {
    const content = <div>{ this.state.clicksNumber }</div> // This is JSX
    return content;
  }
}

const target = document.getElementById('content');
ReactDOM.render(
  <ClickCounterDisplay />,
  target
);
</script>
</body>

</html>

אם אני אשמור את הקוד הזה ואציג אותו, אראה משהו כזה:

0 Clicks
0
Clicks

שזה נחמד אבל די משעמם, כי הקומפוננטה לעולם לא תשתנה. אני ארצה להוסיף כפתור עם + שבכל לחיצה עליו תהיה הוספה ל-state והמספר יעלה ב-1.

הדבר הראשון שאני אוסיף הוא מתודה שתעלה את ה-this.state.clicksNumber ב-1 בקומפוננטת ClickCounter שמכילה את הנתון הזה. טוב, זה קל באמצעות this.setState:

class ClickCounter extends React.Component {
  constructor(props) {
    super(props);     
    this.state = {
      clicksNumber: 0,
    };
  }
  
  clickHandler() {
    this.setState({clicksNumber: this.state.clicksNumber+1});
  }
  
  render() {
    const content = <div>{ this.state.clicksNumber }</div> // This is JSX
    return content;
  }
}

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

class ClickCounter extends React.Component {
  constructor(props) {
    super(props);     
    this.state = {
      clicksNumber: 0,
    };
    this.clickHandler = this.clickHandler.bind(this);
  }
  
  clickHandler() {
    this.setState({clicksNumber: this.state.clicksNumber +1});
  }
  
  render() {
    const content = <div>
            { this.state.clicksNumber }
             <button onClick={this.clickHandler}>+</button>
    </div>
    return content;
  }
}

וזה גם יעבוד.

אבל רגע, חשבתם שזה הכל? חשוב להדגיש שה-onClick הזה הוא לא אירוע native של הדפדפן. אנחנו כאן בJSX ולא ב-HTML. בסוף הכל מרונדר לג׳אווהסקריפט אבל ה-onClick הזה הוא בעצם אירוע סינתטי ולא אירוע ׳אמיתי׳ שמגיע מה-DOM. למה? עניין של ביצועים (לא אחפור יותר מדי) אבל זה ממש חשוב להבנה. למה? כי אם אני רוצה להצמיד אירוע לקומפוננטת ריאקט, זה לא יעבוד. רוצים דוגמה? בטח שאתם רוצים. בואו ובמקום הכפתור המסכן שיש פה ב-JSX:

<button onClick={this.clickHandler}>+</button>

אני אצור קומפוננטה שכל מה שיש בה זה כפתור ואנסה להצמיד אליה אירוע:

class ClickCounterDisplay extends React.Component {
  render() {
    const bigStyle = {
      backgroundColor: 'black',
      borderRadius: 10,
      color: 'silver',
      fontSize: '42px',
      textAlign: 'center',
      width: 250,
    };
    const smallStyle = {
      color: 'gold',
      fontSize: '25px',
    };
    return <div style={bigStyle}>
        <ClickCounter />
        <div style={smallStyle}>Clicks</div>
      </div>
  }
}

class SomeButton extends React.Component {
  render() {
    return <span><button>+</button></span>
  }
}

class ClickCounter extends React.Component {
  constructor(props) {
    super(props);     
    this.state = {
      clicksNumber: 0,
    };
    this.clickHandler = this.clickHandler.bind(this);
  }
  
  clickHandler() {
    this.setState({clicksNumber: this.state.clicksNumber +1});
  }
  
  render() {
    const content = <div>
            { this.state.clicksNumber }
             <SomeButton onClick={this.clickHandler} />
    </div>
    return content;
  }
}

const target = document.getElementById('content');
ReactDOM.render(
  <ClickCounterDisplay />,
  target
);

כפי שאתם רואים יצרתי קומפוננטת SomeButton שאני קורא לה ב-JSX ומנסה להצמיד לה אירוע. מה הבעיה? שזה לא יעבוד. למה זה לא יעבוד? כי ה-onClick הוא אירוע סינתטי ולא אירוע ׳אמיתי׳. אי אפשר להצמיד אותו לקומפוננטות של ריאקט ישירות. אז איך כן עושים את זה? מאוד פשוט – עם handler. כלומר מעבירים את הפונקציה שמופעל מבחוץ כפרמטר. הקומפוננטה SomeButton תראה כך:

class SomeButton extends React.Component {
  render() {
    return <span><button onClick={this.props.clickHandler}>+</button></span>
  }
}

אני מקבל את ה-clickHandler מבחוץ כפרמטר. איך אני מעביר אותו? ובכן, כך:

<SomeButton clickHandler={this.clickHandler} />

See the Pen React component with state management & events on other component by Ran Bar-Zik (@barzik) on CodePen.

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

⚠️אם אהבת את המדריכים על ריאקט – יש ספר מקיף ושלם על ריאקט שכתבתי בשם ללמוד ריאקט בעברית, במסגרת פרויקט עם חברות מובילות ומפתחים אחרים. בספר יש פירוט מקיף יותר על ריאקט ותרגילים רבים ללימוד עצמי. 

פוסטים נוספים שכדאי לקרוא

מיקרו בקרים

בית חכם עם ESPHome ו Home Assistant

הסבר על הום אסיסטנט, מערכת הקוד הפתוח לבית חכם ואיך לחבר אליה מיקרובקרים.

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

הגנה מפני XSS עם Trusted Types

תכונה ב-CSP שמאפשרת מניעה כמעט הרמטית להתקפות XSS שכל מפתח ווב צריך להכיר וכדאי שיכיר.

פתרונות ומאמרים על פיתוח אינטרנט

יישום של nonce על מנת להגן מפני התקפות injection

בפוסט הקודם הסברתי על hash עם CSP על משאבי inline – שזה נחמד ומעולה אבל פחות ישים בעולם האמיתי שבו בדרך כלל התוכן ה-inline (בין

גלילה לראש העמוד