ריאקט למתחילים ממש

17/09/2018

הספריה React שתיכנס בקרוב לגירסא 17 כבר מזמן הפכה מהבטחה למציאות. חברות רבות משתמשות בה כתשתית לפיתוח יישומים מורכבים ופרויקטים רבים חדשים נכתבים בטכנולוגיה זו. אם אתם עדיין רגילים לעבודה עם jQuery ומחפשים נקודת כניסה ידידותית ל React אני מקווה שהפוסט הזה יעזור.

1. המחשבה מאחורי ריאקט

המדרגה הראשונה שתצטרכו להתמודד איתה בעבודה עם ריאקט היא שינוי מחשבתי מאוד גדול לעומת פיתוח HTML/CSS/JavaScript רגיל. בכתיבה הקלאסית לכל טכנולוגיה היה את התפקיד שלה: השתמשנו ב HTML כדי לבנות את התוכן, ב CSS כדי לעצב וב JavaScript כדי להוסיף פעולות.

הדבר איפשר למתכנתים ומעצבים לחלק ביניהם את העבודה בצורה יחסית פשוטה דרך תיאום ממשק שמורכב משמות הקלאסים של האלמנטים. במילים אחרות מעצב היה כותב את ה HTML ו CSS של דף אינטרנט מסוים ומסכם עם המתכנתת שלרכיב מסוים שיהיה אפשר ללחוץ עליו יהיה קלאס בשם clickable-apple. המתכנתת היתה מוסיפה קוד שמטפל באירוע הלחיצה וכך כל אחד מהם יכול לעבוד בנפרד: אם דרוש שינוי בעיצוב למעצב יש חופש פעולה מלא, כל עוד הוא שומר על שם הקלאס. אם דרוש שינוי קוד המתכנתת יכולה לתקן, להוסיף או להוריד פונקציות ככל שתרצה שוב כל עוד שם הקלאס לא משתנה. שמות הקלאסים היו נקודת החיבור בין המעצבים למתכנתים או בין ה HTML/CSS ל JavaScript.

הנה דוגמא פשוטה הכוללת שלושה קבצים משלוש הטכנולוגיות:

<p>
    <input type='text ' placeholder='some text...' class='counted-text' />
    You typed <span class='text-length'>0</span> characters
</p>
.text-length, .counted-text { 
  font-size: 24px;
}
$('.counted-text').on('input', function(e) {
    $('.text-length').text(e.target.value.length);
});

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

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

השינוי הראשון יהיה להוסיף את הכפתור ב HTML ולכן נקבל:

<p>
    <input type='text ' placeholder='some text...' class='counted-text' />
    You typed <span class='text-length'>0</span> characters
  <button class='btn-reset'>Reset</button>
</p>

השינוי השני הוא בקוד ה JavaScript שם נרצה לטפל בלחיצות על הכפתור ולכן נוסיף את הבלוק הבא:

$('.btn-reset').on('click', function(e) {
  $(this).closest('p').find('.counted-text').val('');
});

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

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

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

2. הפקד הראשון שלי בלי JSX

ב React ה HTML וה JavaScript כתובים יחד במבנה שנקרא Component אבל אנחנו כאן בעברית נקרא לו פקד. הפקד הוא יחידה סגורה בעלת אחידות פנימית ושהתוכן שלו נגזר משתי קבוצות משתנים שנקראות State ו Props. ההבדל המרכזי בין כתיבת פקד ריאקט לבין כתיבת קוד jQuery כמו שהיה לנו בדוגמא הקודמת הוא שפקד React עומד בדרך בין הקוד שלנו לבין ה DOM ולא מאפשר לנו לשנות ישירות שום דבר שמופיע על העמוד (*). במקום זה אנחנו משנים את המשתנים שמשפיעים על יצירת הפקד. לאחר שינוי משתנים אלה React יעדכן באופן אוטומטי את העמוד עם כל המידע החדש שבחרנו. בצורה כזו המנגנון של ריאקט שומר עלינו מטעויות כמו זו שעשינו בדוגמא הקודמת. אנחנו לא נוכל לכתוב כפתור שמוחק את הטקסט בתיבה, ולכן במקומו נצטרך לכתוב כפתור שמשנה את המשתנה המתאים מה State. שינוי משתנה זה יגרום להצגת טקסט חדש וגם לחישוב מחדש של אורך הטקסט והצגת האורך המעודכן.

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

(*) אני קצת מרמה כאן- האמת שאפשר ולפעמים גם צריך לעקוף את React ולשנות ישירות את מה שנמצא על המסך. אבל זה כבר סיפור ליום אחר.

בחזרה לקוד ובואו נגיד שלום לפקד הראשון שלנו. תחילה קוד ה HTML הפעם אני מדביק אותו במלואו:

<html>
    <head></head>
    <body>
        <main></main>
    </body>

    <script src='https://cdnjs.cloudflare.com/ajax/libs/react/16.4.2/umd/react.development.js'></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.2/umd/react-dom.development.js'></script>
</html>

במקום לטעון את ספריית jQuery אני טוען הפעם את ספריית React. ספרייה זו מורכבת משני קבצי JavaScript ולכן יש לטעון את שניהם.

עכשיו לחלק המעניין הוא קוד ה JavaScript:

class CountedText extends React.Component {
  render() {
    return React.createElement('p',
                               {},
                               'Hello World'
                              )
  }
}

ReactDOM.render(React.createElement(CountedText), document.querySelector('main'));

קוד JavaScript של ריאקט מתחיל בהגדרת class. פקד הוא מחלקה היורשת מהמחלקה React.Component שהוגדרה בספריית React (אם אתם לא בטוחים איך הגדרת מחלקות ב ES6 עובדת תצטרכו ללמוד על זה בנפרד במקום אחר). למחלקה יש רק מתודה אחת שנקראת render. מתודה זו היא המינימום ההכרחי כדי להגדיר פקד React והיא בעצם מגדירה מה יהיה תוכן הפקד.

היא עושה את זה באמצעות שימוש בפונקציה React.createElement. אפשר לחשוב על פונקציה זו כמו על ה new של פקד: היא מקבלת מחלקה או שם של פקד בפרמטר הראשון, לאחר מכן אוביקט פרמטרים (שנקרא props) ובסוף את הילדים של אותו הפקד. פקד ריאקט מייצג משהו שמופיע ב DOM ולכן בדוגמא הקטנה שלנו תוכן העמוד יהיה אלמנט p יחיד ובתוכו הטקסט Hello World.

אפשר גם לדמיין פקדים יותר מורכבים למשל את הקוד מהדוגמא הקודמת עם תיבת הטקסט, התווית שלידה והכפתור אפשר לכתוב בתור פקד ריאקט באופן הבא:

class CountedText extends React.Component {
  render() {
    return React.createElement(
      'p',
      {},
      [
        React.createElement(
          'input',
          {
            type: 'text',
            placeholder: 'some text...',
            className: 'counted-text',
          },
          null
        ),
        'You typed ',
        React.createElement(
          'span',
          {
            className: 'text-length',
          },
          '0'
        ),
        ' characters',
        React.createElement(
          'button',
          {
            className: 'btn-reset',
          },
          'reset'
        ),
      ]
    )
  }
}

ReactDOM.render(React.createElement(CountedText), document.querySelector('main'));

דרוש קצת מאמץ כדי לקרוא את מה שכתוב שם ולדמיין איך יראה ה HTML. לכן כמעט כל מי שכותב React משתמש בכלי המרה אוטומטי שנקרא Babel ובשפה ייעודית שנקראת JSX.

3. הפקד הראשון שלי עם JSX

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

קוד JSX הוא קוד JavaScript המאפשר להטמיע בתוכו קוד שנראה כמו HTML. תוכנה מיוחדת בשם Babel יודעת לקחת קוד JSX כזה ובאופן אוטומטי להפוך אותו לקוד JavaScript רגיל לגמרי.

הנה לדוגמא הקוד הקודם שכתבנו בגירסת ה JSX שלו:

class CountedText extends React.Component {
  render() {
    return (
      <p>
        <input type='text' placeholder='some text...' className='counted-text' />
        You typed <span className='text-length'>0</span> characters
        <button className='btn-reset'>Reset</button>
      </p>
    )
  }
}

ReactDOM.render(React.createElement(CountedText), document.querySelector('main'));

הקוד הזה הרבה יותר דומה למה שהיינו כותבים ב HTML, אבל לדפדפן שתי הגירסאות יראו בדיוק אותו הדבר. המפתח הוא שלפני שאנחנו שולחים את קובץ ה JS שלנו לדפדפן אנחנו מפעילים את התוכנית Babel שהופכת את ה JSX לכתיב המחלקות הרגיל של JavaScript.

לא אכנס כאן למנגנון ההמרה של Babel וגם לא ארחיב על הכלי webpack בו רבים משתמשים כדי להפעיל את Babel באופן אוטומטי וגם לעשות הרבה יותר. במקום זה אשאיר לכם קישור להורדת קובץ פרויקט בו כל הרכיבים הדרושים כבר מותקנים ומוגדרים:

https://github.com/tocodeil/emotion-demo/archive/master.zip

כדי להשתמש בפרויקט עליכם לכתוב קוד JSX בתוך הקובץ index.js, להתקין node.js אם עדיין לא התקנתם, להפעיל פעם אחת מתוך תיקיית הפרויקט את הפקודה:

$ npm install

ולאחר מכן להפעיל מתוך תיקיית הפרויקט את הפקודה:

$ npm start

עכשיו Babel יקשיב לכל שינוי שתעשו בקובץ index.js ובאופן אוטומטי יהפוך אותו לכתיב מחלקות רגיל. אפשר ללחוץ Ctrl+C בתוך ה cmd כדי לסגור את ההמרה האוטומטית כשאתם מסיימים ואז כשרוצים לחזור לעבוד מקלידים שוב npm start.

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

https://codepen.io/ynonp/pen/xaaYoX

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

עד עכשיו אני לא רואה הרבה סיבות להתרגש מריאקט. בסך הכל כתבנו HTML בצורה מסורבלת יותר. האמת שריאקט מתחיל להיות מוצלח כשמגיעים למנגנונים של טיפול באירועים ולכתיבת קוד ה JavaScript שיפתור לנו את הבעיות שפגשנו בגירסת ה jQuery.

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

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

בריאקט מגדירים את הסטייט בבנאי של המחלקה ולכן הקוד הבא מייצג פקד שיש לו בסטייט משתנה טקסט יחיד:

class CountedText extends React.Component {
  constructor(props) {
    super(props);
    this.state = { text: 'hello world' };
  }

  render() {
    return (
      <p>
        <input type='text' placeholder='some text...' className='counted-text' />
        You typed <span className='text-length'>0</span> characters
        <button className='btn-reset'>Reset</button>
      </p>
    )
  }
}

ReactDOM.render(React.createElement(CountedText), document.querySelector('main'));

נרצה להשתמש במשתנה זה בתוך ה JSX של הפקד כך שבמקום להציג ערך ריק בתיבת הטקסט יוצג תוכן המשתנה ובמקום להציג את המספר 0 בתור אורך הטקסט יוצג האורך האמיתי. ב JSX סימן סוגריים מסולסלים מאפשר לשתול קוד JavaScript רגיל בתוך ה JSX ולכן הקוד הבא מבצע בדיוק את מה שתיארתי:

class CountedText extends React.Component {
  constructor(props) {
    super(props);
    this.state = { text: 'hello world' };
  }

  render() {
    return (
      <p>
        <input
          type='text'
          placeholder='some text...'
          className='counted-text'
          value={this.state.text}
          />

        You typed <span className='text-length'>{this.state.text.length}</span> characters
        <button className='btn-reset'>Reset</button>
      </p>
    )
  }
}

ReactDOM.render(React.createElement(CountedText), document.querySelector('main'));

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

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

ל React מנגנון טיפול באירועים שקצת מזכיר את האירועים של הדפדפן אבל לצערנו הם לא היו עקביים בשמות האירועים. אירוע שינוי טקסט בתיבה נקרא onChange ובעזרתו נוכל לתקן את הפקד ולגרום לו להתחיל לעבוד כמו שעבד בגירסת ה jQuery. עלינו להגדיר פונקציה חדשה בפקד שמשנה את הסטייט ולהפעיל אותה בכל פעם שהתרחש אירוע שינוי בפקד. הקוד נראה כך:

class CountedText extends React.Component {
  constructor(props) {
    super(props);
    this.state = { text: 'hello world' };
    this.setText = this.setText.bind(this);
  }

  setText(e) {
    this.setState({ text: e.target.value });
  }

  render() {
    return (
      <p>
        <input
          type='text'
          placeholder='some text...'
          className='counted-text'
          value={this.state.text}
          onChange={this.setText}
          />

        You typed <span className='text-length'>{this.state.text.length}</span> characters
        <button className='btn-reset'>Reset</button>
      </p>
    )
  }
}

ReactDOM.render(React.createElement(CountedText), document.querySelector('main'));

השינוי התבצע ב-3 מקומות:

  1. הגדרתי פונקציה חדשה במחלקה שנקרא setText ושתופעל בתור Event Handler בתגובה לאירוע שינוי טקסט בתיבה.

  2. הוספתי קריאה ל bind בבנאי של המחלקה כך שהפונקציה תכיר את ה this הנכון בעת הפעלתה.

  3. הוספתי מאפיין onChange לתיבת הטקסט כך שריאקט ידע להפעיל את הפונקציה שלי בכל פעם שמשתמש מנסה לשנות את הטקסט.

התוצאה אומנם נראית שגרתית אבל היא ההיפך הגמור: בכל פעם שמשתמש לוחץ על כפתור בתיבה הטקסט החדש לא נכתב מיד. תחילה מופעלת הפונקציה setText של הפקד, פונקציה זו מעדכנת את המשתנה הפנימי this.state.text לטקסט החדש ורק אחרי עדכון המשתנה הפנימי דרך פונקציה מיוחדת של ריאקט שנקראת setState, רק אז ריאקט יקרא באופן אוטומטי לפונקציה render, ייקח את התוצאה שלה וישתמש בזה כדי לעדכן את מה שמופיע על המסך.

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

נוסיף את ההתנהגות לכפתור Reset כדי לראות את זה. גם כאן התוספת היא ב-3 מקומות: יש ליצור פונקציה חדשה שמאפסת את הטקסט, יש לוודא שהפונקציה החדשה נקראת עם ה this הנכון באמצעות קריאה ל bind בבנאי ובסיום יש להגדיר פעולת onClick לכפתור שתקרא לפונקציה החדשה. הנה הקוד המעודכן:

class CountedText extends React.Component {
  constructor(props) {
    super(props);
    this.state = { text: 'hello world' };
    this.setText = this.setText.bind(this);
    this.reset = this.reset.bind(this);
  }

  setText(e) {
    this.setState({ text: e.target.value });
  }

  reset(e) {
    this.setState({ text: '' });
  }

  render() {
    return (
      <p>
        <input
          type='text'
          placeholder='some text...'
          className='counted-text'
          value={this.state.text}
          onChange={this.setText}
          />

        You typed <span className='text-length'>{this.state.text.length}</span> characters
        <button className='btn-reset' onClick={this.reset}>Reset</button>
      </p>
    )
  }
}

ReactDOM.render(React.createElement(CountedText), document.querySelector('main'));

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

אבל יותר מומלץ שתקלידו את הקוד כמו שהוא לפרויקט מקומי אצלכם על המחשב.

5. חיבור מספר פקדים

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

class App extends React.Component {
  render() {
    return (
      <div>
        <CountedText />
        <CountedText />
        <CountedText />
        <CountedText />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.querySelector('main'));

וזה עובד! באמת ומצייר 4 פעמים את תיבת הטקסט וכל תיבה סופרת את מספר התווים שבה במנותק מהתיבות האחרות והכל בסדר. אבל זה רק משתפר - כי הרבה פעמים נרצה שכל מופע של פקד יהיה קצת שונה מהאחרים, קצת מיוחד. קבוצת המשתנים props עוזרת לנו בדיוק בזה.

אפשר להעביר ערכים ל props בכתיב שמאוד מזכיר העברת Attributes ב HTML. הפקד יוכל לגשת לערכים אלה מתוך פונקציית render שלו כדי להחליט איך בדיוק לצייר את עצמו. למשל נוכל לבקש שרק לחלק מהפקדים יהיה כפתור reset:

class CountedText extends React.Component {
  constructor(props) {
    super(props);
    this.state = { text: 'hello world' };
    this.setText = this.setText.bind(this);
    this.reset = this.reset.bind(this);
  }

  setText(e) {
    this.setState({ text: e.target.value });
  }

  reset(e) {
    this.setState({ text: '' });
  }

  render() {
    return (
      <p>
        <input
          type='text'
          placeholder='some text...'
          className='counted-text'
          value={this.state.text}
          onChange={this.setText}
          />

        You typed <span className='text-length'>{this.state.text.length}</span> characters
        {
          this.props.reset && <button className='btn-reset' onClick={this.reset}>Reset</button>
        }
      </p>
    )
  }
}

class App extends React.Component {
  render() {
    return (
      <div>
        <CountedText reset={true} />
        <CountedText />
        <CountedText />
        <CountedText reset={true} />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.querySelector('main'));

או לחלופין אפשר ליצור פקד חדש רק אם תנאי מסוים מתקיים. הקוד הבא למשל ייצור תיבה חדשה מיד אחרי שהתחלתם לכתוב טקסט בתיבה האחרונה:

class CountedText extends React.Component {
  constructor(props) {
    super(props);
    this.state = { text: '' };
    this.setText = this.setText.bind(this);
    this.reset = this.reset.bind(this);
  }

  setText(e) {
    this.setState({ text: e.target.value });
  }

  reset(e) {
    this.setState({ text: '' });
  }

  render() {
    return (
      <p>
        <input
          type='text'
          placeholder='some text...'
          className='counted-text'
          value={this.state.text}
          onChange={this.setText}
          />

        You typed <span className='text-length'>{this.state.text.length}</span> characters
        <button className='btn-reset' onClick={this.reset}>Reset</button>
        {
          (this.state.text.length > 0) &&
            <CountedText />
        }
      </p>
    )
  }
}

ואפשר לראות אותו בפעולה בקודפן כאן:

6. לאן עכשיו

המעבר מקוד המבוסס על אירועים (משתמש עשה X בתגובה תעשה Y) לקוד דקלרטיבי (מה שמוצג על המסך נראה כך...) דורש שינוי חשיבה מאוד משמעותי אצל מתכנתים והוא הקושי האמיתי של המעבר לריאקט. רק תחשבו איך להציג או להסתיר דיאלוג מודאלי? או איך כפתור בפקד אחד יכול להשפיע על משהו שמוצג בפקד אחר? מצד שני שינוי שיטת החשיבה הזה מאפשר לנו הרבה יותר בקלות להבין מה קוד עושה ואת כל המצבים שרכיב מסוים יכול להופיע בהם רק מלהסתכל על פונקציה אחת - הפונקציה render.

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

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

https://www.tocode.co.il/bundles/react