שלום אורח התחבר

חישוב נתיב לאלמנט ב React

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

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

1המשימה: חישוב הנתיב לאלמנט

נתון עץ הפקדים הבא:

<div>
  <input />
  <Thing mark="5">
    <div>
      <OtherThing mark="8">
        <Thing/>
      </OtherThing>
      <Thing>
        <Thing mark="9" />
      </Thing>
    </div>
  </Thing>
</div>

נרצה לאפשר לפקדים Thing ו OtherThing לגשת לערכי mark במסלול המלא עד אליהם, כך שה Thing הראשון יקבל את הערך 5, ה OtherThing שנמצא בתוכו יקבל את הערכים 5 ו-8 וה Thing השלישי את הערכים 5 ו-9.

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

2ניסיון ראשון: HOC

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

function withMark(Component) {
  return class extends React.Component {
    render() {
      const previousMarks = this.props.previousMarks || [];
      const combinedMarksForChildren = this.props.mark ? [...previousMarks, this.props.mark] : previousMarks;
      const nextMark = this.props.mark ? [...previousMarks, this.props.mark] : previousMarks;

      return (
        <Component {...this.props} mark={nextMark}>
          {React.Children.map(this.props.children, child => (
            React.cloneElement(child, { previousMarks: combinedMarksForChildren })
          ))}
        </Component>
      );
    }
  }
}

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

אבל יש בעיה: ברגע שנוסיף אלמנט שלא קיבל את ה Decorator, אפילו אלמנט רגיל של ריאקט, שברנו את השרשרת והמזהים שנמצאים מעליו לא יעברו לאלמנטים שבתוכו.

3ניסיון שני: context

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

function withMark(Component) {
  const res = class Marker extends React.Component {
    getChildContext() {
      return {
        marks: this.getMarks(),
      }
    }

    getMarks() {
      const previousMarks = this.context.marks || [];
      return this.props.mark ? [...previousMarks, this.props.mark] : previousMarks;
    }

    render() {
      return (
        <Component {...this.props} mark={this.getMarks()} />
      );
    }
  }

  res.childContextTypes = {
    marks: React.PropTypes.array,
  };
  res.contextTypes = {
    marks: React.PropTypes.array,
  };
  return res;
}

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


נהניתם מהפוסט? מוזמנים לשתף ולהגיב