سنناقش في هذا الدرس استخدام خاصيات مكوِّنات React والتي تعرف بالخاصيات props.
ما هي خاصيات المكونات؟
أسهل طريقة لشرح خاصيات المكوِّنات هي القول أنَّها تسلك سلوك خاصيات HTML. بعبارةٍ أخرى، توفِّر الخاصيات خيارات الضبط للمكوِّن. فمثلًا، الشيفرة الآتية فيها المكوِّن Badge الذي يتوقع إرسال الخاصية name
عند تهيئة المكوِّن:
class Badge extends React.Component { render() { return <div>{this.props.name}</div>; } }; class BadgeList extends React.Component { render() { return (<div> <Badge name="Bill" /> <Badge name="Tom" /> </div>); } }; ReactDOM.render(<BadgeList />, document.getElementById('app'));
داخل دالة التصيير للمكوِّن وعندما نستخدم المكوِّن
ستُضاف الخاصية name
إلى المكوِّن بنفس الطريقة التي نضيف فيها خاصية HTML إلى عنصر HTML (أي
)؛ ثم ستستخدم الخاصية name
من المكوِّن Badge
(عبر this.props.name
) كعقدة نصية للعقدة التي ستصيَّر عبر المكون Badge. هذا شبيهٌ بطريقة أخذ العنصر
في HTML الخاصية value
التي ستُستخدَم قيمتها لعرض نص داخل حقل الإدخال.
طريقة أخرى للتفكير في خاصيات المكوِّنات هي تخيلها كأنها قيم لخيارات الضبط المُرسَلة إلى المكوِّن. فإذا نظرتَ إلى نسخة لا تحتوي على صيغة JSX من المثال السابق فسيبدو لك جليًا أنَّ خاصيات المكوِّن ما هي إلا كائنٌ يُمرَّر إلى الخاصية createElement()
(أي React.createElement(Badge, { name: "Bill" })
):
class Badge extends React.Component { render() { return React.createElement( "div", null, // null لم تُعرَّف خاصيات لذا ستكون القيمة this.props.name // كقيمة نصية this.prop.name استخدام ); } }; class BadgeList extends React.Component { render() { return React.createElement( "div", null, React.createElement(Badge, { name: "Bill" }), React.createElement(Badge, { name: "Tom" }) ); } }; ReactDOM.render(React.createElement(BadgeList, null), document.getElementById('app'));
هذا شبيهٌ بطريقة ضبط الخاصيات مباشرةً على عقد React. لكن عند تمرير تعريف المكوِّن Badge إلى الدالة createElement()
بدلًا من قعدة، فستصبح الخاصيات props
متاحةً على المكوِّن نفسه (أي this.props.name
). تُمكِّننا خاصيات المكوِّنات من إعادة استخدام المكوِّن مع أي اسم.
في الشيفرة التي ألقينا إليها نظرةً في هذا القسم، لاحظنا أنَّ المكوِّن BadgeList
يستخدم مكونَي Badge
مع كائن this.props
خاصٌ بكلٍ واحدٍ منها. يمكننا التأكد من ذلك بعرض قيمة this.props
عندما يُهيَّئ المكوِّن Badge
:
class Badge extends React.Component { render() { return <div>{this.props.name}{console.log(this.props)}</div>; } }; class BadgeList extends React.Component { render() { return (<div> <Badge name="Bill" /> <Badge name="Tom" /> </div>); } }; ReactDOM.render(<BadgeList />, document.getElementById('app'));
نلاحظ أنَّ كل نسخة من مكوِّنات React تملك نسخةً خاصةً بها من خاصيةٍ اسمها props
التي تكون كائن JavaScript فارغ، ثم سيُملَأ هذا الكائن عبر المكوِّن الأب باستخدام أي قيمة أو مرجعية في JavaScript، ثم ستُستخدَم هذه القيمة من المكوِّن أو تُمرَّر إلى المكونات الأبناء.
ملاحظات
-
في البيئات التي تستخدم ES5، لن نتمكن من تعديل الخاصية
this.props
لأنها كائنٌ مجمَّد (أيObject.isFrozen(this.props) === true;
). -
يمكنك أن تعدّ
this.props
على أنها كائنٌ للقراءة فقط.
إرسال الخاصيات props إلى مكوِّن
تُرسَل الخاصيات إلى المكوِّن عند إضافة قيم شبيهة بخاصيات HTML إلى المكوِّن عند استخدمه وليس عند تعريفه، فمثلًا، سنجد في الشيفرة الآتية أنَّ المكوِّن Badge قد عُرِّفَ أولًا، ثم أرسلنا خاصيةً له وهي name="Bill"
أثناء استخدامه (أي عند تصيير ``):
class Badge extends React.Component { render() { return <div>{this.props.name}</div>; } }; ReactDOM.render(<Badge name="Bill" />, document.getElementById('app'));
أبقِ في ذهنك أنَّ بإمكاننا إرسال خاصية إلى المكوِّن في أي مكان يمكن أن يُستخدَم المكوِّن فيه. فعلى سبيل المثال، يبيّن المثال من القسم السابقة استخدام المكوِّن Badge
والخاصية name
ضمن المكوِّن BadgeList
:
class Badge extends React.Component { render() { return <div>{this.props.name}</div>; } }; class BadgeList extends React.Component { render() { return (<div> <Badge name="Bill" /> <Badge name="Tom" /> </div> ); } }; ReactDOM.render(<BadgeList />, document.getElementById('app'));
ملاحظات
-
يجب أن تُعدّ خاصيات المكوِّن غيرُ قابلةٍ للتعديل، ويمكن عدم تعديل الخاصيات المُرسَلة من مكوِّن آخر. فلو احتجتَ إلى تعديل قيمة خاصيات أحد المكوِّنات ثم إعادة تصييره، فلا تضبط الخاصيات باستخدام
this.props.[PROP] = [NEW PROP]
.
الحصول على خاصيات المكوِّن
كما ناقشنا في الدرس السابق، يمكن الوصول إلى إلى نسخة المكوِّن من أيٍّ من خيارات الضبط التي تستعمل دالةً عبر الكلمة المحجوزة this
. ففي المثال الآتي استخدمنا الكلمة المحجوزة this
للوصول إلى خاصيات props
المكوِّن Badge
من خيار الضبط render
بكتابة this.props.name
:
class Badge extends React.Component { render() { return <div>{this.props.name}</div>; } }; ReactDOM.render(<Badge name="Bill" />, document.getElementById('app'));
ليس من الصعب معرفة ما يحدث إذا ألقينا نظرةً على شيفرة JavaScript المحوَّلة من JSX:
class Badge extends React.Component { render() { return React.createElement( "div", null, this.props.name ); } }; ReactDOM.render(React.createElement(Badge, { name: "Bill" }), document.getElementById('app'));
أُرسِل الكائن { name: "Bill" }
إلى الدالة createElement()
إضافةً إلى مرجعيةٍ إلى المكوِّن Badge
. القيمة { name: "Bill" }
ستُضبَط كخاصية للمكوِّن قابلةٌ للوصول من الكائن props
، أي أنَّ this.props.name === "Bill"
.
ملاحظات
-
تذكَّر أنَّ
this.props
للقراءة فقط، ولا يجوز ضبط الخاصيات باستخدامthis.props.PROP = 'foo'
.
ضبط قيم افتراضية لخاصيات المكوِّن
يمكن ضبط الخاصيات الافتراضية عند تعريف المكوِّن باستخدام خيار الضبط getDefaultProps
.
المثال الآتي يبيّن كيف عرَّفنا خيار ضبط افتراضي للمكوِّن Badge
للخاصية name
:
class Badge extends React.Component { static defaultProps = { name:'John Doe' } render() { return <div>{this.props.name}</div>; } }; class BadgeList extends React.Component { render() { return (<div> <Badge /> <Badge name="Tom Willy" /> </div>); } }; ReactDOM.render(<BadgeList />, document.getElementById('app'));
ستُضبَط الخاصيات الافتراضية على الكائن this.props
إذا لم تُرسَل خاصيات إلى المكوِّن. يمكنك التحقق من ذلك بملاحظة أنَّ المكوِّن Badge
الذي لم تُضبَط الخاصية name
فيه ستأخذ القيمة الافتراضية 'John Doe'
.
خاصيات المكونات هي أكثر من مجرد سلاسل نصية
قبل أن نلقي نظرةً على التحقق من الخاصيات، علينا أن نستوعب أولًا أنَّ خاصيات المكونات يمكن أن تكون أي قيمة صالحة في JavaScript.
في المثال أدناه، سنضبط عدِّة خاصيات افتراضية تحتوي على مختلف قيم JavaScript:
class MyComponent extends React.Component { static defaultProps = { propArray: [], propBool: false, propFunc: function(){}, propNumber: 5, propObject: {}, propString: 'string' } render() { return (<div> propArray: {this.props.propArray.toString()} <br /><br /> propFunc returns: {this.props.propFunc()} </div>) ; } }; ReactDOM.render(<MyComponent propArray={[1,2,3]} propFunc={function(){return 2;}} />, document.getElementById('app'));
لاحظ كيف أعيدت الكتابة على الخاصيتين propArray
و propObject
مع قيم جديدة عند إنشاء نسخة من المكوِّن MyComponent
.
الفكرة الرئيسية من هذا القسم هو توضيح أنَّنا لسنا محدودين بالقيم النصية عند تمرير قيم للخاصيات.
التحقق من خاصيات المكوِّنات
لاستخدامٍ سليمٍ للخاصيات ضمن المكوِّنات، يجب أن نتحقق من قيمتها عند إنشاء نسخ المكوِّنات.
عند تعريف خيار الضبط propTypes
يمكننا أن نضبط كيف يجب أن تكون الخاصيات وكيف نتحقق منها. سنتحقق في المثال أدناه لنرى إن كانت الخاصيتان propArray
و propObject
من نوع البيانات الصحيح وسنرسلها إلى المكوِّن عند تهيئته:
import PropTypes from 'prop-types'; class MyComponent extends React.Component { static propTypes = { propArray: PropTypes.array.isRequired, propFunc: PropTypes.func.isRequired, } render() { return (<div> propArray: {this.props.propArray.toString()} <br /><br /> propFunc returns: {this.props.propFunc()} </div>); } }; // لهذا المكوِّن خاصياتٌ خطأ ReactDOM.render(<MyComponent propArray={{test:'test'}} />, document.getElementById('app')); // لهذا المكوِّن خاصياتٌ صحيحة // ReactDOM.render(<MyComponent propArray={[1,2]} propFunc={function(){return 3;}} />, document.getElementById('app'));
لم نرسل الخاصيات الصحيحة المُحدَّدة عبر propTypes لتوضيح أنَّ فعل ذلك سيؤدي إلى حدوث خطأ. ستؤدي الشيفرة السابقة إلى ظهور رسالة الخطأ الآتية:
Warning: Failed propType: Invalid prop `propArray` of type `object` supplied to `MyComponent`, expected `array` Warning: Failed propType: Required prop `propFunc` was not specified in `MyComponent`. Uncaught TypeError: this.props.propFunc is not a function
توفِّر React عددًا من المتحققات الداخلية (مثل PropTypes[VALIDATOR]
) والتي سأشرحها بإيجاز فيما يلي، إضافةً إلى إمكانية إنشاء متحققات مخصصة.
(انتقلت إلى مكتبة مختلفة*)
المتحققات الأساسية من الأنواع
React.PropTypes.string | إذا اُستخدِمَت خاصيةٌ، فتحقق أنها سلسلة نصية |
React.PropTypes.bool | إذا اُستخدِمَت خاصيةٌ، فتحقق أنها قيمة منطقية |
React.PropTypes.func | إذا اُستخدِمَت خاصيةٌ، فتحقق أنها دالة |
React.PropTypes.number | إذا اُستخدِمَت خاصيةٌ، فتحقق أنها عدد |
React.PropTypes.object | إذا اُستخدِمَت خاصيةٌ، فتحقق أنها كائن |
React.PropTypes.array | إذا اُستخدِمَت خاصيةٌ، فتحقق أنها مصفوفة |
React.PropTypes.any | إذا اُستخدِمَت خاصيةٌ، فتحقق أنها من أي نوع من الأنواع |
متحققات القيم المطلوبة
React.PropTypes.[TYPE].isRequired
|
إضافة .isRequired إلى أي نوع من المتحققات سيؤدي إلى جعل الخاصية مطلوبةً (مثال ذلك propTypes:{propFunc:React.PropTypes.func.isRequired}) |
متحققات العناصر
React.PropTypes.element | الخاصية هي عنصر React. |
React.PropTypes.node | أي شيء يمكن تصييره: الأرقام، أو السلاسل النصية، أو العناصر، أو مصفوفة تحتوي هذه الأنواع |
المتحققات المتعددة
React.PropTypes.oneOf(['Mon','Fri'])
|
الخاصية هي أحد أنواع القيم المُحدَّدة |
React.PropTypes.oneOfType([React.PropTypes.string,React.PropTypes.number])
|
الخاصية هي كائن يمكن أن يكون أحد أنواع القيم المُحدَّدة |
متحققات المصفوفات والكائنات
React.PropTypes.arrayOf(React.PropTypes.number)
|
الخاصية هي مصفوفة تحتوي على نوع واحد من القيم |
React.PropTypes.objectOf(React.PropTypes.number)
|
هي كائن يحتوي على أحد أنواع القيم |
React.PropTypes.instanceOf(People)
|
هي كائن يكون نسخةً من دالةٍ بانية معينة (كما في الكلمة المحجوزة instanceof )
|
React.PropTypes.shape({color:React.PropTypes.string,size: React.PropTypes.number})
|
هي كائن يحتوي على خاصيات من أنواعٍ معينة |
المتحققات المخصصة
function(props, propName, componentName){}
|
توفير دالة خاصة بك للتحقق |
ترجمة -وبتصرف- للفصل React Component Properties من كتاب React Enlightenment
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.