הסאגה של React ו ref

15/11/2018

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

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

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

1. ריאקט 0.14

כבר בתחילת הדרך של ריאקט בתור פרויקט קוד פתוח היה ברור הצורך לשלב קוד ריאקט עם קוד חיצוני ולכן לגשת ישירות ל DOM Elements שריאקט מנהל. בואו נניח שאנחנו רוצים כפתור שיעביר את ה Keyboard Focus בין תיבות טקסט. בריאקט 0.14 יכלנו לכתוב את הקוד הבא:

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

  1. הגדרת ref בתור מחרוזת על כל אחד מהאלמנטים שצריכים לגשת אליו בגישה ישירה:
<input type='text' ref='a' />
  1. שליפת ה ref מתוך אוביקט הפקד ושימוש בו כדי להגיע ל DOM Node המתאים:
    const ref = this.refs[activeRefName];
    const el = ReactDOM.findDOMNode(ref);

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

בנוסף השימוש ב findDOMNode היה נראה לרבים מיותר.

2. ריאקט 15

בריאקט 15 החליטו לוותר על הפונקציה findDOMNode ולהתיחס ל ref בתור DOM Nodes ממש. זה אומר שהקוד הבא הוא גירסת ריאקט 15 לאותה תוכנית מחליפת פוקוס שראינו קודם:

הפעם כבר לא צריך להשתמש ב findDOMNode ואפשר לגשת ישירות ל ref:

    const el = this.refs[activeRefName];

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

מבחינת הקוד אנחנו מגדירים בבנאי מערך בשם inputs, בפונקציית render משתמשים ב ref כדי למלא את המערך באופן הבא:

      <input type='text' ref={(el) => { this.inputs.push(el); }} />

ובפונקציה switchFocus אנחנו לא צריכים יותר את המיפוי ויכולים לגשת ישירות לאלמנטים במערך:

  switchFocus() {
    const nextActive = (this.state.active + 1) % refNames.length;
    this.setState({ active:  nextActive });
    const el = this.inputs[nextActive];
    el.focus();
  }

3. ריאקט 16.3

וזה עבד ממש טוב - חוץ מהכתיב המסורבל כי עכשיו צריך כל פעם לרשום פונקציה מלאה. ריאקט 16 הוסיף שני מנגנונים חדשים שעוזרים לנו לכתוב ref טובים יותר: האחד נקרא createRef והשני forwardRef.

נתחיל עם createRef - מנגנון זה נועד כדי שנוכל מצד אחד לשמור את ה refs במשתנים במחלקה לפי בחירה שלנו (כמו בהגדרת פונקציה), אבל מצד שני הכתיב יהיה הרבה יותר קל מאשר כתיב של פונקציה מלאה.

כך נראה הקוד אחרי השינוי ל createRef:

הפעם אנחנו יוצרים בבנאי את כל אוביקטי ה ref מראש באמצעות השורה:

    this.inputs = [
      React.createRef(),
      React.createRef(),
      React.createRef(),
      React.createRef(),
    ];

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

<input type='text' ref={this.inputs[0]} />

הבעיה היחידה בכתיב זה היא שאנחנו כבר לא יכולים להתיחס ל ref ישירות בתור DOM Node וצריכים לקבל את ה DOM Node מתוך ה ref, קצת בדומה למה ש findDOMNode עשתה בעבר הרחוק. הקוד לגשת ל DOM Node נראה כעת כך:

    const el = this.inputs[nextActive].current;

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

רוצים לשמוע עוד על ref ושאר המנגנונים והשינויים שריאקט עוברת? היום זאת ההזדמנות שלכם. בשעה 10:00 יתקיים כאן וובינר בדיוק על הדברים האלה ועדיין אפשר להצטרף. פרטים בקישור:

https://www.tocode.co.il/workshops/54