העברת קלט לפקד באמצעות Props

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

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

1. הגדרת PropTypes

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

propTypes: {
    name: React.PropTypes.string.isRequired,
},

וקוד הפקד המלא שמשתמש ב PropTypes נראה כך:

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

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

2. סוגי קלטים

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

React.createClass({
  propTypes: {
    // You can declare that a prop is a specific JS primitive. By default, these
    // are all optional.
    optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,

    // Anything that can be rendered: numbers, strings, elements or an array
    // (or fragment) containing these types.
    optionalNode: React.PropTypes.node,

    // A React element.
    optionalElement: React.PropTypes.element,

    // You can also declare that a prop is an instance of a class. This uses
    // JS's instanceof operator.
    optionalMessage: React.PropTypes.instanceOf(Message),

    // You can ensure that your prop is limited to specific values by treating
    // it as an enum.
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

    // An object that could be one of many types
    optionalUnion: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number,
      React.PropTypes.instanceOf(Message)
    ]),

    // An array of a certain type
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

    // An object with property values of a certain type
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

    // An object taking on a particular shape
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),

    // You can chain any of the above with `isRequired` to make sure a warning
    // is shown if the prop isn't provided.
    requiredFunc: React.PropTypes.func.isRequired,

    // A value of any data type
    requiredAny: React.PropTypes.any.isRequired,

    // You can also specify a custom validator. It should return an Error
    // object if the validation fails. Don't `console.warn` or throw, as this
    // won't work inside `oneOfType`.
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error(
          'Invalid prop `' + propName + '` supplied to' +
          ' `' + componentName + '`. Validation failed.'
        );
      }
    },

    // You can also supply a custom validator to `arrayOf` and `objectOf`.
    // It should return an Error object if the validation fails. The validator
    // will be called for each key in the array or object. The first two
    // arguments of the validator are the array or object itself, and the
    // current item's key.
    customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
      if (!/matchme/.test(propValue[key])) {
        return new Error(
          'Invalid prop `' + propFullName + '` supplied to' +
          ' `' + componentName + '`. Validation failed.'
        );
      }
    })
  },
  /* ... */
});

3. העברת קלטים בין פקדים

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

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

var Rect = React.createClass({
    propTypes: {
        color: React.PropTypes.string,
    },
    render: function() {
        var rectStyle = { background: this.props.color, width: '200px', height: '200px', display: 'inline-block' }
        return <div style={rectStyle}></div>;
    },
});

אגב שימו לב להגדרת כללי העיצוב. ראינו כבר שאפשר להשתמש במאפיין className כדי לקבוע CSS Class לפקד. כשמגדירים לפקד Inline Style במקום להעביר את ההגדרות בתור טקסט ריאקט מצפה להעברת אוביקט. כל שדה באוביקט מתאים למאפיין CSS, כאשר מקפים בשם המאפיין מוחלפים לאות גדולה ראשונה במילה הבאה (כך נכתוב marginTop במקום margin-top). בצורה כזו קל יותר לכתוב הגדרות סגנון המושפעות ממשתנים, לדוגמא צבע הנגזר מקלט לפקד.

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

4. תקציר הפקודות שהוצגו בפרק

קוד הפקד שכתבנו המשתמש ב Prop Types ומגדיר ערכי ברירת מחדל:

var HelloWorld = React.createClass({
  propTypes: {
    name: React.PropTypes.string
  },

  getDefaultProps() {
    return {
      name: 'Guest'
    };
  },

  render() {

    return (<div className="hello-world">
        <p>Hello World: {this.props.name} </p>
      </div>)
  }
});

קוד הפקד על קודפן במסך עריכה אינטרקטיבי:
http://codepen.io/ynonp/pres/ZOxGKj


קוד הפקד שכתבנו המשתמש ב Prop Types ומגדיר ערכי ברירת מחדל:

var HelloWorld = React.createClass({
  propTypes: {
    name: React.PropTypes.string
  },

  getDefaultProps() {
    return {
      name: 'Guest'
    };
  },

  render() {

    return (<div className="hello-world">
        <p>Hello World: {this.props.name} </p>
      </div>)
  }
});

קוד הפקד על קודפן במסך עריכה אינטרקטיבי: http://codepen.io/ynonp/pres/ZOxGKj