استخدام المراجع مع DOM
تُزوّدنا المراجع بطريقة للوصول إلى عقد DOM أو عناصر React المُنشأة باستخدام التابع render
.
تكون الخاصيّات في تدفّق بيانات React النموذجي هي الطريقة الوحيدة التي يتواصل بها المُكوِّن الأب مع مُكوِّناته الأبناء، ولتعديل الابن نُعيد تصييره باستخدام الخاصيّات الجديدة. على الرغم من ذلك هنالك حالات نحتاج فيها بشكلٍ إجباري إلى تعديل المُكوِّنات الأبناء خارج هذا التدفّق النموذجي. لتعديل المُكوِّن الابن يجب أن يكون نسخة (instance) من مُكوِّن React أو عنصر DOM. ولكلتا الحالتين تُعطينا React خطة للالتفاف حول هذا الموضوع.
متى نستخدم المراجع (Refs)
هنالك بعض الحالات المناسبة لاستخدام المراجع (refs
) وهي:
- إدارة التركيز على العناصر (focus)، واختيار النصوص، والتحكم بتشغيل الوسائط.
- إطلاق التحريكات الإجباريّة.
- التكامل مع مكتبات طرف ثالث تتعامل مع DOM.
تجنّب استخدام المراجع لأي شيء يُمكِن فعله بشكلٍ إلزامي. فمثلًا بدلًا من تعريض التوابع open()
و close()
في مُكوِّن مربّع الحوار Dialog
، مرِّر الخاصيّة isOpen
له.
لا تُفرِط في استخدام المراجع
ربّما تكون رغبتك الأولى لاستخدام المراجع هي تحقيق كل ما تُريده في تطبيقك. إن كانت هذه حالتك فخُذ لحظة للتفكير حول المكان المُلائم لوضع الحالة في التسلسل الهرمي للمُكوِّنات. يتضح عادةً أنّ المكان المناسب لوضع الحالة هو في المستويات العليا من التسلسل الهرمي للمُكوِّنات. انظر إلى قسم رفع الحالات المشتركة للمستوى الأعلى للمزيد من المعلومات.
ملاحظة: حدّثنا الأمثلة التالية لكي تستخدم واجهة برمجة التطبيق React.createRef()
التي قُدِّمَت في إصدار React 16.3. إن كُنتَ تستخدم إصدار أقدم من React نُوصي باستخدام ردود نداء المراجع بدلًا من ذلك.
إنشاء المراجع
تُنشَأ المراجع باستخدام React.createRef()
وتُربَط إلى عناصر React عبر الخاصيّة ref
. تُعيَّن المراجع إلى نسخة من الخاصيّة عند بناء المُكوِّنات لكي نرجع إليها عبر المُكوِّن:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
الوصول للمراجع
عند تمرير المرجع إلى عنصر في التابع render
، يُصبِح المرجع إلى العقدة قابلًا للوصول باستخدام الخاصّية current
للمرجع:
const node = this.myRef.current;
تختلف قيمة المرجع بناءً على نوع العقدة:
- عند استخدام الخاصيّة
ref
على عنصر HTML، تستقبل هذه الخاصيّةref
المُنشَأة في الدالة البانية باستخدامReact.createRef()
عنصر DOM كخاصيّة حاليّةcurrent
. - عند استخدام الخاصيّة
ref
على مُكوِّن صنف مُخصَّص، يستقبل الكائنref
نسخة من المُكوِّن كخاصيّة حاليّةcurrent
. - لا يُمكنك استخدام الخاصيّة
ref
على المُكوِّنات المُعرَّفة كدوال لأنّها لا تملك نُسَخ (instances).
يُوضِّح المثال التالي الفروقات.
إضافة مرجع إلى عنصر DOM
تستخدم هذه الشيفرة الخاصيّة ref
لتخزين مرجع إلى عقدة DOM:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
// إنشاء مرجع لتخزين عقدة DOM وهي textInput
this.textInput = React.createRef();
this.focusTextInput = this.focusTextInput.bind(this);
}
focusTextInput() {
// التركيز على الحقل النصي باستخدام DOM API
// ملاحظة: نحن نصل إلى الخاصيّة current للحصول على عقدة DOM
this.textInput.current.focus();
}
render() {
// إخبار React أننا نريد ربط مرجع العنصر input
// مع textInput التي أنشأناها في الدالة البانية
return (
<div>
<input
type="text"
ref={this.textInput} />
<input
type="button"
value="ركز على الحقل النصي"
onClick={this.focusTextInput}
/>
</div>
);
}
}
ستُعيِّن React الخاصيّة current
إلى عنصر DOM عندما وصل المُكوِّن (mount)، وتُعيِّنها إلى القيمة null
عند فصل المُكوِّن (unmount). تحصل تحديثات ref
قبل خُطافات دورة حياة المُكوِّن componentDidMount
أو componentDidUpdate
.
إضافة مرجع إلى مُكوِّن الصنف
إن أردنا تغليف المُكوِّن CustomTextInput
السّابق لمُحاكاة النقر عليها فورًا بعد الوصل (mounting)، فبإمكاننا استخدام المرجع ref
للوصول إلى حقل الإدخال المُخصَّص واستدعاء تابعه focusTextInput
بشكل يدوي:
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.focusTextInput();
}
render() {
return (
<CustomTextInput ref={this.textInput} />
);
}
}
لاحظ أنّ هذا يعمل فقط إن كان المُكوِّن CustomTextInput
مُعرَّفًا كصنف:
class CustomTextInput extends React.Component {
// ...
}
المراجع والمكونات الدالية
لا يُمكنِك استخدام الخاصيّة ref
على المُكوِّنات الداليّة لأنّها لا تمتلك نُسَخ:
function MyFunctionalComponent() {
return <input />;
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
// لن يعمل هذا
return (
<MyFunctionalComponent ref={this.textInput} />
);
}
}
ينبغي تحويل المُكوِّن إلى صنف أن أردت إضافة مرجع له، تمامًا كما تفعل عندما تحتاج توابع دورة الحياة أو الحالة.
على أيّة حال تستطيع استخدام الخاصيّة ref
بداخل المُكوِّن المُعرَّف كدالة طالما تُشير إلى عنصر DOM أو مُكوِّن مُعرَّف كصنف:
function CustomTextInput(props) {
// يجب تعريف textInput هنا لكي يستطيع المرجع الرجوع إليها
let textInput = React.createRef();
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="ركز على الحقل النصي"
onClick={handleClick}
/>
</div>
);
}
تعريض مراجع DOM للمكون الأب
قد ترغب في بعض الحالات النادرة الوصول إلى عقدة DOM للمُكوِّن الابن من خلال المُكوِّن الأب. لا يُفضَّل فعل ذلك بشكل عام لأنّه قد يخرق تغليف المُكوِّن، ولكن قد يكون أحيانًا مفيدًا لإطلاق حدث التركيز أو قياس حجم أو موضع عقدة DOM للمُكوِّن الابن.
وبينما تستطيع إضافة مرجع إلى المُكوِّن الابن فليس هذا الحل الأمثل، حيث ستحصل فقط على نسخة عن المُكوِّن بدلًا من عقدة DOM. ولا يعمل هذا أيضًا على المُكوِّنات الدالية.
إن كُنتَ تستخدم إصدار React 16.3 أو أحدث، فنوصي باستخدام تمرير المراجع لأجل هذه الحالات. حيث يُتيح ذلك للمُكوِّنات أن تُعرِّض أي مرجع للمُكوِّن الابن كمرجع خاص بها. ستجد مثالًا مُفصّلًا حول كيفيّة تعريض عقدة DOM للابن إلى المُكوِّن الأب في صفحة تمرير المراجع من هذا التوثيق.
إن كُنتَ تستخدم إصدار React 16.2 أو أقدم، أو احتجتَ مرونة أكبر من تلك التي يُعطيك إيّاها تمرير المراجع، فتستطيع استخدام هذه المقاربة المختلفة لتمرير مرجع كخاصيّة ذات اسم مُختلِف.
ننصح بقدر الإمكان تجنّب تعريض عُقَد DOM، ولكن قد يكون أحيانًا هذا حلًّا جيّدًا للالتفاف حول هذه المشكلة. لاحظ حاجة هذه المقاربة إلى إضافة بعض الشيفرة إلى المُكوِّن الابن. إن لم يكن لديك أي تحكّم بالمُكوِّن الابن فخيارك الأخير هو استخدام findDOMNode()
، ولكن لا نُوصي بهذا.
ردود نداء المراجع
تدعم React أيضًا طريقة أخرى لتعيين المراجع تُدعى ردود نداء المراجع (callback refs)، والتي تُعطي درجة أكبر من التحكم عند تعيين وإزالة تعيين المراجع.
بدلًا من تمرير الخاصيّة ref
المُنشَأة من قبل التابع createRef()
، مرِّر دالة. تستقبل هذه الدالة نسخة من مُكوِّن React أو عنصر DOM كوسائط لها، والتي يُمكِن تخزينها والوصول إليها من مكان آخر.
يعتمد المثال التالي نمطًا شائعًا: وهو استخدام رد نداء ref
لتخزين مرجع إلى عقدة DOM في نسخة من الخاصيّة:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInputRef = element => {
this.textInput = element;
};
this.focusTextInput = () => {
// التركيز على الحقل النصي باستخدام DOM API
if (this.textInput) this.textInput.focus();
};
}
componentDidMount() {
// التركيز التلقائي على الحقل النصي عند وصل المُكوِّن (mount)
this.focusTextInput();
}
render() {
// استخدم رد نداء المرجع لتخزين مرجع إلى عنصر DOM للحقل النصي
// في نسخة من الحقل (مثل this.textInput)
return (
<div>
<input
type="text"
ref={this.setTextInputRef}
/>
<input
type="button"
value="التركيز على الحقل النصي"
onClick={this.focusTextInput}
/>
</div>
);
}
}
ستستدعي React رد نداء ref
عن طريق عنصر DOM عند وصل المُكوِّن (mount)، وتستدعيه مع القيمة null
عند فصل المُكوِّن (unmount). نضمن أن تكون قيمة المراجع مُحدَّثة قبل إطلاق توابع المُكوِّن componentDidMount
أو componentDidUpdate
.
تستطيع تمرير ردود نداء المراجع بين المُكوِّنات كما تفعل بين كائنات المراجع التي يُنشِئها React.createRef()
:
function CustomTextInput(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
class Parent extends React.Component {
render() {
return (
<CustomTextInput
inputRef={el => this.inputElement = el}
/>
);
}
}
في المثال السّابق يُمرِّر المُكوِّن Parent
رد نداء المرجع من خلال الخاصيّة inputRef
إلى المُكوِّن CustomTextInput
والذي يُمرِّر نفس الدالة كخاصيّة ref
إلى العنصر <input>
، وكنتيجة لذلك تُعيَّن this.inputElement
في المُكوِّن Parent
إلى عقدة DOM المُوافِقة للعنصر <input>
في المُكوِّن CustomTextInput
.
واجهة برمجة التطبيق API القديمة: مراجع السلاسل النصية
إن تعاملتَ مع React سابقًا فقد تكون متآلفًا مع واجهة برمجة التطبيق (API) القديمة حيث كانت الخاصيّة ref
عبارة عن سلسلة نصيّة، مثل textInput
، ونصل إلى عقدة DOM عبر this.refs.textInput
. لا ننصح بفعل ذلك لأنّ مراجع السلاسل النصيّة لديها بعض المشاكل، وأصبحت مُهمَلة في React، ومن المحتمل أنّها ستُزال في الإصدارات القادمة.
ملاحظة: إن كنتَ تستخدم this.refs.textInput
حاليًّا للوصول إلى المراجع، فيُفضَّل أن تستخدم إمّا نمط ردود النداء أو createRef API بدلًا من ذلك.
محاذير استخدام ردود نداء المراجع
إن كان رد نداء المرجع مُعرَّفًا كدالة سطريّة (inline)، فسيُستدعى مرتين خلال التحديثات، أول مرة مع القيمة null
ومرة أخرى مع عنصر DOM، وهذا بسبب إنشاء نسخة جديدة من الدالة مع كل تصيير، لذا تحتاج React إلى مسح المرجع القديم وإعداد واحد جديد. بإمكانك تجنّب ذلك عن طريق تعريف رد نداء المرجع كتابع مربوط في الصنف، ولكن لاحظ أنّ هذا غير هام في معظم الحالات.
انظر أيضًا
- شرح JSX بالتفصيل
- التحقق من الأنواع الثابتة
- التحقق من الأنواع باستخدام PropTypes
- استخدام المراجع مع DOM (الصفحة الحالية)
- المكونات غير المضبوطة
- تحسين الأداء
- React بدون ES6
- React بدون JSX
- المطابقة (Reconciliation)
- استخدام السياق (Context) في React
- استخدام الأجزاء (Fragments) في React
- المداخل (Portals) في React
- حدود الأخطاء
- مكونات الويب
- المكونات ذات الترتيب الأعلى
- تمرير المراجع
- خاصيات التصيير
- تكامل React مع المكتبات الأخرى
- سهولة الوصول
- تقسيم الشيفرة
- الوضع الصارم (Strict Mode)