الفرق بين المراجعتين لصفحة: «React/render props»
Kinan-mawed (نقاش | مساهمات) لا ملخص تعديل |
تحديث |
||
(6 مراجعات متوسطة بواسطة 3 مستخدمين غير معروضة) | |||
سطر 1: | سطر 1: | ||
<noinclude>{{DISPLAYTITLE:خاصيات التصيير}}</noinclude> | <noinclude>{{DISPLAYTITLE:خاصيات التصيير في React}}</noinclude> | ||
يُشير مصطلح خاصيّة التصيير (render prop) إلى تقنية بسيطة لمشاركة الشيفرة بين مكوّنات React باستخدام خاصية والتي قيمتها هي عبارة عن دالة. | يُشير مصطلح [https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce خاصيّة التصيير (render prop)] إلى تقنية بسيطة لمشاركة الشيفرة بين مكوّنات React باستخدام خاصية والتي قيمتها هي عبارة عن دالة. | ||
يأخذ المكوّن الذي يمتلك خاصيّة تصيير دالة تُعيد عنصر React ويستدعيها بدلًا من تنفيذ منطق التصيير الخاص به:<syntaxhighlight lang="javascript"> | يأخذ المكوّن الذي يمتلك خاصيّة تصيير دالة تُعيد عنصر React ويستدعيها بدلًا من تنفيذ منطق التصيير الخاص به:<syntaxhighlight lang="javascript"> | ||
سطر 7: | سطر 7: | ||
)}/> | )}/> | ||
</syntaxhighlight>تتضمّن المكتبات التي تستخدم خاصيّات التصيير React Router و Downshift. | </syntaxhighlight>تتضمّن المكتبات التي تستخدم خاصيّات التصيير [https://reacttraining.com/react-router/web/api/Route/Route-render-methods React Router] و [https://github.com/paypal/downshift Downshift] و [https://github.com/jaredpalmer/formik Formik]. | ||
سنناقش في هذه الصفحة فائدة خاصيّات التصيير وكيفية كتابة خاصيّات التصيير الخاصّة بك. | سنناقش في هذه الصفحة فائدة خاصيّات التصيير وكيفية كتابة خاصيّات التصيير الخاصّة بك. | ||
سطر 31: | سطر 31: | ||
render() { | render() { | ||
return ( | return ( | ||
<div style={{ height: ' | <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}> | ||
<h1>حرك الفأرة</h1> | <h1>حرك الفأرة</h1> | ||
<p>موقع الفأرة الحالي هو ({this.state.x}, {this.state.y})</p> | <p>موقع الفأرة الحالي هو ({this.state.x}, {this.state.y})</p> | ||
سطر 38: | سطر 38: | ||
} | } | ||
} | } | ||
</syntaxhighlight>بينما يتحرك المؤشر على الشاشة، يعرض المكوّن إحداثياته <code>(x, y)</code> ضمن العنصر <code><nowiki><p></nowiki></code>. | </syntaxhighlight>بينما يتحرك المؤشر على الشاشة، يعرض المكوّن إحداثياته <code>(x, y)</code> ضمن العنصر <code><nowiki><p></nowiki></code>. | ||
سطر 61: | سطر 62: | ||
render() { | render() { | ||
return ( | return ( | ||
<div style={{ height: ' | <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}> | ||
{/* ... ولكن كيف نصير شيء آخر غير العنصر <p>? */} | {/* ... ولكن كيف نصير شيء آخر غير العنصر <p>? */} | ||
<p>موقع الفأرة الحالي هو: ({this.state.x}, {this.state.y})</p> | <p>موقع الفأرة الحالي هو: ({this.state.x}, {this.state.y})</p> | ||
</div> | |||
); | |||
} | |||
} | |||
class MouseTracker extends React.Component { | |||
render() { | |||
return ( | |||
<> | |||
<h1>Move the mouse around!</h1> | |||
<Mouse /> | |||
</> | |||
); | |||
} | |||
} | |||
</syntaxhighlight>يُغلِّف المكوّن <code><Mouse></code> الآن السلوك المرتبط بالاستماع إلى أحداث تحريك الفأرة <code>mousemove</code> وتخزين إحداثيات <code>(x, y)</code> لموقع المؤشر، ولكنّه ليس قابلًا لإعادة الاستخدام حتى الآن. | |||
فلنفترض أننا نمتلك المكوّن <code><Cat></code> والذي يُصيِّر صورة لقطة تُطارِد الفأرة ضمن الشاشة. بإمكاننا استخدام الخاصيّة <code><Cat mouse = { { x, y } } ></code> لإخبار المكوّن بإحداثيات الفأرة بحيث تعلم أي تضع الصورة في الشاشة. | |||
كمحاولة أولى جرّب تصيير المكوّن <code><Cat></code> بداخل تابع التصيير للمكوّن <code><Mouse></code> كما يلي:<syntaxhighlight lang="javascript"> | |||
class Cat extends React.Component { | |||
render() { | |||
const mouse = this.props.mouse; | |||
return ( | |||
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} /> | |||
); | |||
} | |||
} | |||
class MouseWithCat extends React.Component { | |||
constructor(props) { | |||
super(props); | |||
this.handleMouseMove = this.handleMouseMove.bind(this); | |||
this.state = { x: 0, y: 0 }; | |||
} | |||
handleMouseMove(event) { | |||
this.setState({ | |||
x: event.clientX, | |||
y: event.clientY | |||
}); | |||
} | |||
render() { | |||
return ( | |||
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}> | |||
{/* | |||
كان بإمكاننا فقط تبديل العنصر p بالمكون <Cat> هنا | |||
ولكن سيتوجب علينا إنشاء مكون <MouseWithSomethingElse> منفصل | |||
في كل مرة نحتاج لاستخدامه. لذا لن يكون <MouseWithCat> قابلًا لإعادة الاستخدام حتى الآن | |||
*/} | |||
<Cat mouse={this.state} /> | |||
</div> | </div> | ||
); | ); | ||
سطر 71: | سطر 125: | ||
class MouseTracker extends React.Component { | class MouseTracker extends React.Component { | ||
render() { | |||
return ( | |||
<div> | |||
<h1>Move the mouse around!</h1> | |||
<MouseWithCat /> | |||
</div> | |||
); | |||
} | |||
} | |||
</syntaxhighlight>ستعمل هذه الطريقة لحالتنا، لكننا لم نحقق حتى الآن هدفنا الحقيقي من تغليف هذا السلوك بطريقة قابلة لإعادة الاستخدام، ففي كل مرة نريد فيها الحصول على موقع الفأرة لحالة استخدام مختلفة يجب علينا إنشاء مكوّن جديد (وهو <code><MouseWithCat></code>) والذي يُصيِّر شيء ما مُخصَّص لتلك الحالة. | |||
هنا تأتي فائدة خاصيّات التصيير، فبدلًا من كتابة المكوّن <code><Cat></code> بشكل حرفي في المكوّن <code><Mouse></code> وتغيير ناتجه، بإمكاننا تزويد المكوّن <code><Mouse></code> بخاصيّة على شكل دالة تستخدمها لتحدد بشكل ديناميكي ما هي خاصيّة التصيير:<syntaxhighlight lang="javascript"> | |||
class Cat extends React.Component { | |||
render() { | |||
const mouse = this.props.mouse; | |||
return ( | |||
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} /> | |||
); | |||
} | |||
} | |||
class Mouse extends React.Component { | |||
constructor(props) { | |||
super(props); | |||
this.handleMouseMove = this.handleMouseMove.bind(this); | |||
this.state = { x: 0, y: 0 }; | |||
} | |||
handleMouseMove(event) { | |||
this.setState({ | |||
x: event.clientX, | |||
y: event.clientY | |||
}); | |||
} | |||
render() { | |||
return ( | |||
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}> | |||
{/* | |||
بدلًا من تزويد تمثيل ثابت لما يصيره المكون <Mouse> | |||
استخدم خاصية التصيير لتحدد بشكل دينامكي ما الذي ينبغي تصييره | |||
*/} | |||
{this.props.render(this.state)} | |||
</div> | |||
); | |||
} | |||
} | |||
class MouseTracker extends React.Component { | |||
render() { | |||
return ( | |||
<div> | |||
<h1>Move the mouse around!</h1> | |||
<Mouse render={mouse => ( | |||
<Cat mouse={mouse} /> | |||
)}/> | |||
</div> | |||
); | |||
} | |||
} | |||
</syntaxhighlight>والآن بدلًا من نسخ المكوّن <code><Mouse></code> وكتابة شيفرة حرفية بداخل التابع <code>render</code> الخاص به لحل المشكلة لحالة محددة، فسنُزوِّد خاصيّة <code>render</code> والتي يستخدمها <code><Mouse></code> ليحدد بشكل ديناميكي ما الذي ينبغي تصييره. | |||
إنّ خاصيّة التصيير هي بشكل مبسّط خاصيّة على شكل دالة يستخدمها المكوّن ليعلم ما يجب تصييره. | |||
تجعل هذه التقنية من السلوك الذي نحتاجه للمشاركة قابلًا للنقل بسهولة. للحصول على هذا السلوك صيِّر المكوّن <code><Mouse></code> مع خاصيّة <code>render</code> والتي تخبره ما يجب عليه تصييره مع الإحداثيات الحالية للمؤشر. | |||
من الأشياء المثيرة للاهتمام التي تميّز خاصيّات التصيير هي أنّه بإمكانك تطبيقه على [[React/higher order components|المكوّنات ذات الترتيب الأعلى]] باستخدام مكوّن اعتيادي مع خاصيّة تصيير. على سبيل المثال إن كنت تفضل أن يكون لديك المكوّن ذو الترتيب الأعلى <code>withMouse</code> بدلًا من المكوّن <code><Mouse></code> فبإمكانك بسهولة إنشاء واحد باستخدام المكوّن <code><Mouse></code> مع خاصيّة تصيير:<syntaxhighlight lang="javascript"> | |||
// إن أردت حقا مكون ذو ترتيب أعلى فبإمكانك بسهولة | |||
// إنشاء واحد باستخدام مكوّن عادي مع خاصية تصيير | |||
function withMouse(Component) { | |||
return class extends React.Component { | |||
render() { | |||
return ( | |||
<Mouse render={mouse => ( | |||
<Component {...this.props} mouse={mouse} /> | |||
)}/> | |||
); | |||
} | |||
} | |||
} | |||
</syntaxhighlight>لذا من الممكن باستخدام خاصية التصيير الانتقال إلى نمط آخر. | |||
== استخدام خاصيات أخرى غير render == | |||
من الهام معرفة أنّه ليس بالضرورة إذا كان اسم هذا النمط خاصيّات التصيير (render props) أن تستخدم الخاصيّة التي اسمها <code>render</code>. فبالحقيقة [https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce أي دالة يستخدمها المكوّن ليعرف ما الذي ينبغي تصييره هي عمليًّا خاصيّة تصيير]. | |||
على الرغم من استخدام المثال السابق للاسم <code>render</code> فبإمكاننا استخدام أي اسم مثل الخاصيّة <code>children</code>:<syntaxhighlight lang="javascript"> | |||
<Mouse children={mouse => ( | |||
<p>موقع الفأرة هو: {mouse.x}, {mouse.y}</p> | |||
)}/> | |||
</syntaxhighlight>ليس من الضروري أن تكون الخاصيّة <code>children</code> موجودة في قائمة الخاصيّات في عنصر JSX لديك، فبإمكانك وضعها بشكل مباشر بداخل العنصر:<syntaxhighlight lang="javascript"> | |||
<Mouse> | |||
{mouse => ( | |||
<p>موقع الفأرة هو: {mouse.x}, {mouse.y}</p> | |||
)} | |||
</Mouse> | |||
</syntaxhighlight>سترى هذه الطريقة مستخدمة في <code>[https://github.com/chenglou/react-motion react-motion]</code> API. | |||
بما أنّ هذه الطريقة غير معتادة قليلًا فقد ترغب بالإعلان عن أنّ <code>children</code> يجب أن تكون دالة من خلال <code>propTypes</code>:<syntaxhighlight lang="javascript"> | |||
Mouse.propTypes = { | |||
children: PropTypes.func.isRequired | |||
}; | |||
</syntaxhighlight> | |||
== محاذير == | |||
=== انتبه عند استخدام خاصية التصيير مع <code>React.PureComponent</code> === | |||
قد يلغي استخدام خاصية التصيير الفائدة المرجوة من استخدام <code>[https://reactjs.org/docs/react-api.html#reactpurecomponent React.PureComponent]</code> إن أنشأت الدالة بداخل التابع <code>render</code>، وذلك لأنّ المقارنة بين الخاصيّات ستعيد دومًا <code>false</code> للخاصيّات الجديدة، وسيُولِّد كل تابع <code>render</code> في هذه الحالة قيمة جديدة للخاصيّة <code>render</code>. | |||
فمثلًا بالمتابعة مع المكوّن السابق <code><Mouse></code>، إن كان هذا المكوّن يمتد إلى <code>React.PureComponent</code> بدلًا من <code>React.Component</code>، فسيبدو مثالنا كما يلي:<syntaxhighlight lang="javascript"> | |||
class Mouse extends React.PureComponent { | |||
// نفس التنفيذ السابق | |||
} | |||
class MouseTracker extends React.Component { | |||
render() { | |||
return ( | |||
<div> | |||
<h1>حرّك الفأرة</h1> | |||
{/* | |||
هذا سيّء! حيث ستكون قيمة خاصية التصيير | |||
مختلفة عند كل تصيير | |||
*/} | |||
<Mouse render={mouse => ( | |||
<Cat mouse={mouse} /> | |||
)}/> | |||
</div> | |||
); | |||
} | |||
} | |||
</syntaxhighlight>في هذا المثال عند كل تصيير للمكوّن <code><MouseTracker></code> فسيُولِّد دالة جديدة كقيمة للخاصية <code><Mouse render></code>، وبذلك يلغي تأثير المكوّن <code><Mouse></code> الذي يمتد إلى <code>React.PureComponent</code> في المقام الأول. | |||
لحل هذه المشكلة تستطيع تعريف الخاصيّة كنسخة من تابع كما يلي:<syntaxhighlight lang="javascript"> | |||
class MouseTracker extends React.Component { | |||
// معرف كنسخة تابع | |||
// يشير this.renderTheCat دومًا إلى نفس الدالة عند استخدامها للتصيير | |||
renderTheCat(mouse) { | |||
return <Cat mouse={mouse} />; | |||
} | |||
render() { | render() { | ||
return ( | return ( | ||
<div> | <div> | ||
<h1>حرك الفأرة</h1> | <h1>حرك الفأرة</h1> | ||
<Mouse /> | <Mouse render={this.renderTheCat} /> | ||
</div> | </div> | ||
); | ); | ||
سطر 81: | سطر 282: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight>في الحالات التي لا تستطيع فيها تعريف الخاصيّة بشكل ثابت (مثلًا إذا كنت تحتاج إلى تغيير خاصية أو حالة المكوّن) فيجب أن يمتد المكوّن <code><Mouse></code> إلى <code>React.Component</code> بدلًا من ذلك. | ||
== انظر أيضًا == | |||
* [[React/jsx in depth|شرح JSX بالتفصيل]] | |||
* [[React/static type checking|التحقق من الأنواع الثابتة]] | |||
* [[React/typechecking with proptypes|التحقق من الأنواع باستخدام PropTypes]] | |||
* [[React/refs and the dom|استخدام المراجع مع DOM]] | |||
* [[React/uncontrolled components|المكونات غير المضبوطة]] | |||
* [[React/optimizing performance|تحسين الأداء]] | |||
* [[React/react without es6|React بدون ES6]] | |||
* [[React/react without jsx|React بدون JSX]] | |||
* [[React/reconciliation|المطابقة (Reconciliation)]] | |||
* [[React/context|استخدام السياق (Context) في React]] | |||
* [[React/fragments|استخدام الأجزاء (Fragments) في React]] | |||
* [[React/portals|المداخل (Portals) في React]] | |||
* [[React/error boundaries|حدود الأخطاء]] | |||
* [[React/web components|مكونات الويب]] | |||
* [[React/higher order components|المكونات ذات الترتيب الأعلى]] | |||
* [[React/forwarding refs|تمرير المراجع]] | |||
* [[React/integrating with other libraries|تكامل React مع المكتبات الأخرى]] | |||
* [[React/accessibility|سهولة الوصول]] | |||
* [[React/code splitting|تقسيم الشيفرة]] | |||
* [[React/strict mode|الوضع الصارم (Strict Mode)]] | |||
== مصادر== | |||
*[https://reactjs.org/docs/render-props.html صفحة خاصيات التصيير في توثيق React الرسمي]. | |||
[[تصنيف:React]] | |||
[[تصنيف:React Advanced Guides]] |
المراجعة الحالية بتاريخ 10:33، 4 نوفمبر 2020
يُشير مصطلح خاصيّة التصيير (render prop) إلى تقنية بسيطة لمشاركة الشيفرة بين مكوّنات React باستخدام خاصية والتي قيمتها هي عبارة عن دالة.
يأخذ المكوّن الذي يمتلك خاصيّة تصيير دالة تُعيد عنصر React ويستدعيها بدلًا من تنفيذ منطق التصيير الخاص به:
<DataProvider render={data => (
<h1>أهلًا {data.target}</h1>
)}/>
تتضمّن المكتبات التي تستخدم خاصيّات التصيير React Router و Downshift و Formik.
سنناقش في هذه الصفحة فائدة خاصيّات التصيير وكيفية كتابة خاصيّات التصيير الخاصّة بك.
استخدام خاصيّات التصيير للاهتمامات المشتركة
تُعد المكوّنات الوحدة الأساسية من الشيفرة القابلة لإعادة الاستخدام في React، ولكن ليس من الواضح كيفيّة مشاركة الحالة أو السلوك من مكوّن إلى مكوّن آخر يحتاج نفس الحالة.
على سبيل المثال يتتبع المكوّن التالي موقع الفأرة في تطبيق الويب:
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
<h1>حرك الفأرة</h1>
<p>موقع الفأرة الحالي هو ({this.state.x}, {this.state.y})</p>
</div>
);
}
}
بينما يتحرك المؤشر على الشاشة، يعرض المكوّن إحداثياته (x, y)
ضمن العنصر <p>
.
السؤال الآن هو كيفيّة إعادة استخدام هذا السلوك في مكوّن آخر؟ أي بمعنى آخر إن احتاج مكوّن آخر إلى معرفة مكان المؤشر، فهل نستطيع تغليف هذا السلوك لمشاركته بسهولة مع ذلك المكوّن؟
لمّا كانت المكوّنات الوحدة الأساسية في React لإعادة استخدام الشيفرة، فلنجرّب إعادة ترتيب الشيفرة قليلًا لتستخدم المكوّن <Mouse>
والذي يُغلِّف السلوك الذي نريد إعادة استخدامه في مكان ما:
// يغلف المكون <Mouse> السلوك الذي نحتاجه
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
{/* ... ولكن كيف نصير شيء آخر غير العنصر <p>? */}
<p>موقع الفأرة الحالي هو: ({this.state.x}, {this.state.y})</p>
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<>
<h1>Move the mouse around!</h1>
<Mouse />
</>
);
}
}
يُغلِّف المكوّن <Mouse>
الآن السلوك المرتبط بالاستماع إلى أحداث تحريك الفأرة mousemove
وتخزين إحداثيات (x, y)
لموقع المؤشر، ولكنّه ليس قابلًا لإعادة الاستخدام حتى الآن.
فلنفترض أننا نمتلك المكوّن <Cat>
والذي يُصيِّر صورة لقطة تُطارِد الفأرة ضمن الشاشة. بإمكاننا استخدام الخاصيّة <Cat mouse = { { x, y } } >
لإخبار المكوّن بإحداثيات الفأرة بحيث تعلم أي تضع الصورة في الشاشة.
كمحاولة أولى جرّب تصيير المكوّن <Cat>
بداخل تابع التصيير للمكوّن <Mouse>
كما يلي:
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class MouseWithCat extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
{/*
كان بإمكاننا فقط تبديل العنصر p بالمكون <Cat> هنا
ولكن سيتوجب علينا إنشاء مكون <MouseWithSomethingElse> منفصل
في كل مرة نحتاج لاستخدامه. لذا لن يكون <MouseWithCat> قابلًا لإعادة الاستخدام حتى الآن
*/}
<Cat mouse={this.state} />
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<MouseWithCat />
</div>
);
}
}
ستعمل هذه الطريقة لحالتنا، لكننا لم نحقق حتى الآن هدفنا الحقيقي من تغليف هذا السلوك بطريقة قابلة لإعادة الاستخدام، ففي كل مرة نريد فيها الحصول على موقع الفأرة لحالة استخدام مختلفة يجب علينا إنشاء مكوّن جديد (وهو <MouseWithCat>
) والذي يُصيِّر شيء ما مُخصَّص لتلك الحالة.
هنا تأتي فائدة خاصيّات التصيير، فبدلًا من كتابة المكوّن <Cat>
بشكل حرفي في المكوّن <Mouse>
وتغيير ناتجه، بإمكاننا تزويد المكوّن <Mouse>
بخاصيّة على شكل دالة تستخدمها لتحدد بشكل ديناميكي ما هي خاصيّة التصيير:
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
{/*
بدلًا من تزويد تمثيل ثابت لما يصيره المكون <Mouse>
استخدم خاصية التصيير لتحدد بشكل دينامكي ما الذي ينبغي تصييره
*/}
{this.props.render(this.state)}
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
</div>
);
}
}
والآن بدلًا من نسخ المكوّن <Mouse>
وكتابة شيفرة حرفية بداخل التابع render
الخاص به لحل المشكلة لحالة محددة، فسنُزوِّد خاصيّة render
والتي يستخدمها <Mouse>
ليحدد بشكل ديناميكي ما الذي ينبغي تصييره.
إنّ خاصيّة التصيير هي بشكل مبسّط خاصيّة على شكل دالة يستخدمها المكوّن ليعلم ما يجب تصييره.
تجعل هذه التقنية من السلوك الذي نحتاجه للمشاركة قابلًا للنقل بسهولة. للحصول على هذا السلوك صيِّر المكوّن <Mouse>
مع خاصيّة render
والتي تخبره ما يجب عليه تصييره مع الإحداثيات الحالية للمؤشر.
من الأشياء المثيرة للاهتمام التي تميّز خاصيّات التصيير هي أنّه بإمكانك تطبيقه على المكوّنات ذات الترتيب الأعلى باستخدام مكوّن اعتيادي مع خاصيّة تصيير. على سبيل المثال إن كنت تفضل أن يكون لديك المكوّن ذو الترتيب الأعلى withMouse
بدلًا من المكوّن <Mouse>
فبإمكانك بسهولة إنشاء واحد باستخدام المكوّن <Mouse>
مع خاصيّة تصيير:
// إن أردت حقا مكون ذو ترتيب أعلى فبإمكانك بسهولة
// إنشاء واحد باستخدام مكوّن عادي مع خاصية تصيير
function withMouse(Component) {
return class extends React.Component {
render() {
return (
<Mouse render={mouse => (
<Component {...this.props} mouse={mouse} />
)}/>
);
}
}
}
لذا من الممكن باستخدام خاصية التصيير الانتقال إلى نمط آخر.
استخدام خاصيات أخرى غير render
من الهام معرفة أنّه ليس بالضرورة إذا كان اسم هذا النمط خاصيّات التصيير (render props) أن تستخدم الخاصيّة التي اسمها render
. فبالحقيقة أي دالة يستخدمها المكوّن ليعرف ما الذي ينبغي تصييره هي عمليًّا خاصيّة تصيير.
على الرغم من استخدام المثال السابق للاسم render
فبإمكاننا استخدام أي اسم مثل الخاصيّة children
:
<Mouse children={mouse => (
<p>موقع الفأرة هو: {mouse.x}, {mouse.y}</p>
)}/>
ليس من الضروري أن تكون الخاصيّة children
موجودة في قائمة الخاصيّات في عنصر JSX لديك، فبإمكانك وضعها بشكل مباشر بداخل العنصر:
<Mouse>
{mouse => (
<p>موقع الفأرة هو: {mouse.x}, {mouse.y}</p>
)}
</Mouse>
سترى هذه الطريقة مستخدمة في react-motion
API.
بما أنّ هذه الطريقة غير معتادة قليلًا فقد ترغب بالإعلان عن أنّ children
يجب أن تكون دالة من خلال propTypes
:
Mouse.propTypes = {
children: PropTypes.func.isRequired
};
محاذير
انتبه عند استخدام خاصية التصيير مع React.PureComponent
قد يلغي استخدام خاصية التصيير الفائدة المرجوة من استخدام React.PureComponent
إن أنشأت الدالة بداخل التابع render
، وذلك لأنّ المقارنة بين الخاصيّات ستعيد دومًا false
للخاصيّات الجديدة، وسيُولِّد كل تابع render
في هذه الحالة قيمة جديدة للخاصيّة render
.
فمثلًا بالمتابعة مع المكوّن السابق <Mouse>
، إن كان هذا المكوّن يمتد إلى React.PureComponent
بدلًا من React.Component
، فسيبدو مثالنا كما يلي:
class Mouse extends React.PureComponent {
// نفس التنفيذ السابق
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>حرّك الفأرة</h1>
{/*
هذا سيّء! حيث ستكون قيمة خاصية التصيير
مختلفة عند كل تصيير
*/}
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
</div>
);
}
}
في هذا المثال عند كل تصيير للمكوّن <MouseTracker>
فسيُولِّد دالة جديدة كقيمة للخاصية <Mouse render>
، وبذلك يلغي تأثير المكوّن <Mouse>
الذي يمتد إلى React.PureComponent
في المقام الأول.
لحل هذه المشكلة تستطيع تعريف الخاصيّة كنسخة من تابع كما يلي:
class MouseTracker extends React.Component {
// معرف كنسخة تابع
// يشير this.renderTheCat دومًا إلى نفس الدالة عند استخدامها للتصيير
renderTheCat(mouse) {
return <Cat mouse={mouse} />;
}
render() {
return (
<div>
<h1>حرك الفأرة</h1>
<Mouse render={this.renderTheCat} />
</div>
);
}
}
في الحالات التي لا تستطيع فيها تعريف الخاصيّة بشكل ثابت (مثلًا إذا كنت تحتاج إلى تغيير خاصية أو حالة المكوّن) فيجب أن يمتد المكوّن <Mouse>
إلى React.Component
بدلًا من ذلك.
انظر أيضًا
- شرح JSX بالتفصيل
- التحقق من الأنواع الثابتة
- التحقق من الأنواع باستخدام PropTypes
- استخدام المراجع مع DOM
- المكونات غير المضبوطة
- تحسين الأداء
- React بدون ES6
- React بدون JSX
- المطابقة (Reconciliation)
- استخدام السياق (Context) في React
- استخدام الأجزاء (Fragments) في React
- المداخل (Portals) في React
- حدود الأخطاء
- مكونات الويب
- المكونات ذات الترتيب الأعلى
- تمرير المراجع
- تكامل React مع المكتبات الأخرى
- سهولة الوصول
- تقسيم الشيفرة
- الوضع الصارم (Strict Mode)