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

למאמר הקודם בסדרה על ריאקט
למאמר הבא בסדרה על ריאקט

במאמרים הקודמים למדנו על קומפוננטות. אבל מה הבעיה עם הקומפוננטות האלו? הן די פשוטות ומשמשות להצגת תוכן בלבד בעוד שרוב הקומפוננטות שנרצה ליצור מחייבות איזושהי אינטראקציה וחייבות לאכסן בתוכן איזשהו מידע שמשתנה. המידע יכול להגיע מבחוץ, יכול להגיע מבפנים או מכל מקום אחר, אבל הקומפוננטה חייבת להכיל מידע ולאכסן אותו. זה נקרא state. כאשר קומפוננטות פשוטות המשמשות לתצוגה בלבד נקראות stateless וקומפוננטות שצריכות לשמור בתוכן מידע הן קומפוננטות שמחייבות state. מבהיל? לא ממש. אם אתם יודעים לעבוד עם תכונות, ולמדנו את זה באחד המאמרים הקודמים, תדעו להסתדר עם state. בגדול, מדובר באובייקט שמכיל מידע ואת המידע הזה אפשר לשנות.

בואו ונדגים, הכל יותר פשוט עם דוגמה. נניח ואתם עובדים בחברת פח לשיווק מוצר סייברי כושל. למשל בזק שרוצה להציג קומפוננטה שמציגה את מספר התקפות הסייבר שיש במדינה. מומחי החברה (כלומר מנהל השיווק אחרי שאכטה טובה) בדקו ומצאו שמדובר ב-1200 התקפות. טוב, זה די קל למימוש. ניצור שתי קומפוננטות. אחת CurrentCyberAttackDisplay שעוטפת את המספר ומספקת עיצוב והשניה CurrentCyberAttackCounter שמספקת אך ורק את המספר. הראשונה תכלול את השניה. איך הקוד יראה? כך:

class CurrentCyberAttackDisplay 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}>
        <CurrentCyberAttackCounter />
        <div style={smallStyle}>Cyber attacks per day</div>
      </div>
  }
}

class CurrentCyberAttackCounter extends React.Component {
  render() {
    const content = <div>1200</div> // This is JSX
    return content;
  }
}

const target = document.getElementById('content');
ReactDOM.render(
  <CurrentCyberAttackDisplay />,
  target
);
1200 Cyber attacks per day קומפוננטת ריאקט

1200 Cyber attacks per day קומפוננטת ריאקט

בשלב הזה של המדריכים, אתם אמורים להבין מעולה את הקוד שלעיל,לא הבנתם? זה הזמן לחזור על המאמרים הקודמים.

מה הבעיה עם הקומפוננטה הזו? מגיע מנהל השיווק ואומר שהוא רוצה שהמספר הזה יעלה בכל 20 שניות ב-100, כדי להלחיץ ולהכניס דינמיות וכדי שהלקוח יילחץ ויזמין את מוצר הסייבר סייברי של החברה. איך עושים את זה? אין לי דרך לעשות את זה אלא באמצעות state . כלומר משתנה בקומפוננטת CurrentCyberAttackCounter שיקבל 1200 והרצה של מתודה שתעלה את המשתנה הזה ב-100 בכל 20 שניות. איך עושים את זה?

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

בתוך הקונסטרקטור שיהיה ב-CurrentCyberAttackCounter אנחנו נכניס 1200 ל-state.

class CurrentCyberAttackCounter extends React.Component {
  constructor(props) {
    super(props);     
    this.state = {
      attackNumber: 1200,
    };
  }
  
  render() {
    const content = 
{ this.state.attackNumber }
// This is JSX return content; } }

זה… זה די פשוט, נכון? לא שונה מתצוגה. אבל עכשיו יש לנו משתנה שאנחנו יכולים לפעול עליו. איך? נכתוב מתודה פשוטה בשם timerTick שמעלה את this.state.attackNumber ב-10 בדיוק.

class CurrentCyberAttackCounter extends React.Component {
  constructor(props) {
    super(props);     
    this.state = {
      attackNumber: 1200,
    };
  }
  
  timerTick() {
    this.setState({attackNumber: this.state.attackNumber + 10});
  }
  
  render() {
    const content = 
{ this.state.attackNumber }
// This is JSX return content; } }

זה נהדר אבל מי בדיוק יקרא למתודה הזו? אם אף אחד לא יפעיל את timerTick היא תשכב לה כאבן שאין לה הופכין. אנחנו צריכים פונקציה שתפעל מייד כשהקומפוננטה מתחילה להתרנדר. בדיוק בשביל זה יש לנו את ה-API של ריאקט. בדיוק כמו render, יש לנו את componentDidMount שרצה אך ורק כשהקומפוננטה מוכנה לפעולה. שם נשים את כל מה שאנחנו רוצים לפעול מייד אחרי שהקומפוננטה עובדת. במקרה שלנו, את הקריאה ל-timerTick. או יותר נכון, נשתמש ב-setInterval כדי לקרוא ל-timerTick בכל שניה (1000 מילישניות). איך זה יראה? בדיוק ככה:

class CurrentCyberAttackCounter extends React.Component {
  constructor(props) {
    super(props);     
    this.state = {
      attackNumber: 1200,
    };
  }
  
  timerTick() {
    this.setState({attackNumber: this.state.attackNumber + 10});
  }
  
  componentDidMount() {
    setInterval(this.timerTick, 1000);
  }
  
  render() {
    const content = 
{ this.state.attackNumber }
// This is JSX return content; } }

רגע, רגע – יש עוד משהו אחר אחרון. אם אני אריץ את הקוד לעיל הוא לא יעבוד ואקבל שגיאה.

Uncaught TypeError: Cannot read property 'attackNumber' of undefined     at timerTick

Uncaught TypeError: Cannot read property ‘attackNumber’ of undefined
at timerTick

למה? כי כש-timerTick רצה בסקופ של setInterval, היא לא תוכל לגשת ל-this. אנחנו חייבים לעשות לה bind באמצעות:


this.timerTick = this.timerTick.bind(this);

אם אתם מבינים למה, מעולה. אם לא – סימן שאתם צריכים לחזור על סקופינג כי זה מאוד חשוב. במאמר הזה אני כותב על כך. זה חובה להבין למה יש bind כי אנחנו עובדים המון עם סקופינג בריאקט (ובכלל בג׳אווהסקריפט מודרני) וחייבים לדעת את זה אחרת תחטפו שגיאה מימין ומשמאל. הכלל הוא: פונקציה/מתודה שאתם משתמשים בה לא יודעת להגיע למשתנה? יש לכם בעיית סקופינג וצריכים להשתמש ב-bind או ב-call\apply.

והנה, זה הקוד המושלם.

class CurrentCyberAttackDisplay 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 
Cyber attacks per day
} } class CurrentCyberAttackCounter extends React.Component { constructor(props) { super(props);      this.state = { attackNumber: 1200, }; this.timerTick = this.timerTick.bind(this); } timerTick() { this.setState({attackNumber: this.state.attackNumber + 10}); } componentDidMount() { setInterval(this.timerTick, 1000); } render() { const content =
{ this.state.attackNumber }
// This is JSX return content; } } const target = document.getElementById('content'); ReactDOM.render( , target );

והוא גם יעבוד. נסו ותהנו:

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

אז הנה, באמצעות דוגמה אחת הראינו איך עובדים עם stateful components בריאקט. למדנו על constructor שהוא חשוב לאיתחול ה-state ועל componentDidMount שבאמצעותו ניהלנו את ה-state. במאמר הבא נדבר על אירועים בקומפוננטות ריאקט ונעשה דברים קצת יותר אינטראקטיביים. מבטיח.

אהבתם? לא אהבתם? דרגו!

לא אהבתי בכלללא אהבתיבסדראהבתיאהבתי מאוד (2 הצבעות, ממוצע: 5.00 מתוך 5)

תגיות: פורסם בקטגוריה: ריאקט

יאללה, שתפו :)

אל תשארו מאחור! יש עוד מה ללמוד!

8 comments on “ריאקט – קומפוננטות עם סטייטים
  1. מוטי הגיב:

    חשוב לציין ש setState היא אסינכרונית

  2. שלום ברים הגיב:

    לא עדיף, במקום ה-bind המכוער, להשתמש ב-arrow function?
    setInterval(() => this.timerTick, 1000);

    • רן בר-זיק הגיב:

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

  3. היי רן,
    כמו שמוטי מעליי ציין, פונקצית setState הינה פונקציה אסינכרונית אשר שימוש בה יכול לגרום לתופעות לא רצויות.
    כאשר נרצה לעדכן את אחד הStateים על סמך הערך הקודם של אותו הState נעשה זאת על ידי העברה של פונקצית עדכון לתוך setState אשר מקבלת את הState ה-“ישן” ומחזירה אובייקט אשר מכיל את השינוי.

    כך לדוגמה נוכל לכתוב את המתודה timerTick כך:

    this.setState((prevState) => ({attackNumber: prevState.attackNumber + 10}))

    בצורה זאת נוכל להימנע מעדכון לא נכון של הState

כתיבת תגובה

האימייל לא יוצג באתר.

רישום