التحقق من الأنواع باستخدام PropTypes في React

من موسوعة حسوب

ملاحظة: انتقلت React.PropTypes إلى حزمة مختلفة منذ إصدار React v15.5. من فضلك استخدم المكتبة prop-types بدلًا من ذلك. نُزوِّدك بشيفرة جاهزة لأتمتة هذه العملية.

عندما ينمو تطبيقك قد تلتقط العديد من الأخطاء باستخدام التحقق من الأنواع. بإمكانك استخدام إضافات JavaScript مثل Flow أو TypeScript للتحقّق من كامل تطبيقك، ولكن حتى ولو لم تستخدمها، تمتلك React بعض القدرات المُضمَّنة للتحقّق من الأنواع. لإجراء التحقّق من الأنواع على خاصيّات المُكوّنات بإمكانك تعيين الخاصية propTypes إليها:

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>أهلًا {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};

سنسنخدم في هذا المثال صنف المكونات، بيد أنّه يمكن إنجاز الوظيفة نفسها بالمكونات الدوالية، أو المكونات المُنشأة بواسطة React.memo أو React.forwardRef.

تستخرج PropTypes مجموعة من أدوات التحقّق لتحرص على أن تكون البيانات المستقبلة صحيحة. سنستخدم في هذا المثال PropTypes.string. عندما نعطي قيمة غير صحيحة للخاصية سيظهر خطأ في نافذة الكونسول. ولأسباب تتعلق بالأداء لا تُستخدَم propTypes إلا في وضع التطوير.

PropTypes

سنُوثِّق في هذا المثال أدوات التحقّق المختلفة الموجودة:

import PropTypes from 'prop-types';

MyComponent.propTypes = {
  // بإمكانك التصريح عن نوع الخاصيّة. افتراضيًّا كل هذه التصاريح اختياريّة
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // أي شيء يُمكِن تصييره: الأعداد، والسلاسل النصيّة، والعناصر، أو حتى مصفوفة (أو قطعة) تحتوي على هذه الأنواع
  optionalNode: PropTypes.node,

  //  عنصر React
  optionalElement: PropTypes.element,

  //  نوع عنصر React
  optionalElementType: PropTypes.elementType,

  // بإمكانك أيضًا التصريح على أنّ الخاصيّة هي نسخة من الصنف
  optionalMessage: PropTypes.instanceOf(Message),

  // بإمكانك أن تضمن أن تكون خاصيّاتك محدود إلى قيم مُعيّنة عن طريق معاملتها كـ enum
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // كائن قد يكون واحدًا من عدّة أنواع
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // مصفوفة من نوع مُحدّد
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // كائن  من قيم خاصيّات ذات نوع مُحدّد
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // كائن يتخذ شكلًا مُحدّدًا
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),

  // extra properties كائن مع تنبيه على الخاصيات الإضافية 
  optionalObjectWithStrictShape: PropTypes.exact({
    name: PropTypes.string,
    quantity: PropTypes.number
  }),   

  // بإمكانك إضافة isRequired إلى أي من الأمثلة السابقة لإعطاء تحذير إن لم تعطى الخاصيّة
  requiredFunc: PropTypes.func.isRequired,

  // قيمة من أي نوع من البيانات
  requiredAny: PropTypes.any.isRequired,

  // بإمكانك أيضًا تحديد أداة تحقق مخصصة. ينبغي أن تعيد كائن يحتوي على الخطأ إن فشل التحقّق.
  // لا تستخدم console.warn أو الكلمة throw
  // حيث لن يعمل هذا بداخل 'oneOfType'
  customProp: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'خاصيّة خاطئة `' + propName + '` معطاة إلى' +
        ' `' + componentName + '`. فشل التحقق'
      );
    }
  },

  // بإمكانك أيضًا تزويد أداة تحقق مخصصة من أجل 'arrayOf' و 'objectOf'
  // ينبغي أن تعيد كائن يحتوي على الخطأ إن فشل التحقق
  // ستستدعى أداة التحقق لكل مفتاح في المصفوفة أو الكائن.
  // أول وسيطين من أداة التحقق هما المصفوفة أو الكائن ومفتاح العنصر الحالي
  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'خاصيّة خاطئة `' + propFullName + '` معطاة إلى' +
        ' `' + componentName + '`. فشل التحقق'
      );
    }
  })
};

تمرير عنصر ابن وحيد

نستطيع باستخدام PropTypes.element أن نُحدِّد إمكانية تمرير عنصر ابن وحيد للمُكوِّن:

import PropTypes from 'prop-types';

class MyComponent extends React.Component {
  render() {
	// يجب أن يكون هذا عنصرًا واحدًا فقط أو سيظهر تحذير
    const children = this.props.children;
    return (
      <div>
        {children}
      </div>
    );
  }
}

MyComponent.propTypes = {
  children: PropTypes.element.isRequired
};

قيم الخاصية الافتراضية

يمكنك تعريف قيم افتراضية من أجل props عبر الإسناد إلى خاصية خاصة تدعى defaultProps:

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

// props تحديد القيم الافتراضية من أجل:
Greeting.defaultProps = {
  name: 'غريب'
};

// "يصير إلى "مرحبًا، غريب:
ReactDOM.render(
  <Greeting />,
  document.getElementById('example')
);

إن كنت تستخدم إضافات التحويل في Babel مثل transform-class-properties فباستطاعتك التصريح عن defaultProps كخاصية ثابتة بداخل صنف مُكوِّن React، ومع ذلك هذه الصياغة غير مكتملة وتتطلب خطوة للتصريف لتعمل بداخل المتصفح. للمزيد من المعلومات انظر إلى proposal-class-fields:

class Greeting extends React.Component {
  static defaultProps = {
    name: 'غريب'
  }

  render() {
    return (
      <div>مرحبًا ،{this.props.name}</div>
    )
  }
}

تُستخدَم defaultProps في المثال السابق لتضمن وجود قيمة افتراضية للخاصية this.props.name إن لم يُحدِّدها المكون الأب. يحصل التحقّق من الأنواع باستخدام PropTypes بعد إضافة القيم الافتراضيّة defaultProps لذلك يُطبَّق عليها أيضًا.

انظر أيضًا

مصادر