كيفية البدء سريعًا باستخدام VUE3.0: أدخل وتعلم
في مشاريع React، هناك العديد من السيناريوهات التي تتطلب Ref
. على سبيل المثال، استخدم السمة ref
للحصول على عقدة DOM والحصول على نسخة كائن ClassComponent؛ استخدم الخطاف useRef
لإنشاء كائن Ref لحل مشكلة عدم قدرة setInterval
على الحصول على أحدث حالة، ويمكنك أيضًا استدعاء React.createRef
طريقة لإنشاء كائن Ref
يدويًا.
على الرغم من سهولة استخدام Ref
، إلا أنه لا يزال من المحتم مواجهة مشكلات في المشاريع الفعلية. ستقوم هذه المقالة بفرز المشكلات المختلفة المتعلقة Ref
من منظور الكود المصدري وتوضيح ما يتم فعله خلف واجهات برمجة التطبيقات ذات الصلة ref
. بعد قراءة هذه المقالة، قد يكون لديك فهم أعمق Ref
.
أولاً، ref
هو اختصار reference
، وهو مرجع. في ملف تعريف نوع react
، يمكنك العثور على العديد من الأنواع ذات الصلة بالمرجع، وهي مدرجة هنا.
RefObject<T> { للقراءة فقط: T null } واجهة MutableRefObject<T> {current: T; }
RefObject
{ current: T }
الخطاف useRef
، يتم إرجاع RefObject/MutableRefObejct ، سوف يحذر Typescript ⚠️ إذا تم تعديل refObject.current
.
مرجع const = useRef<string>(خالي) ref.current = '' // خطأ
TS: لا يمكن تعيينه إلى "الحالي" لأنه خاصية للقراءة فقط.
null
إلى تعريف طريقة useRef
. عندما لا تحتوي المعلمة العامة الواردة T
على null
، يتم إرجاع RefObject<T>
MutableRefObject<T>
وظيفة useRef<T>(initialValue: T): MutableRefObject<T>; function useRef<T>(initialValue: T | null): RefObject<T>؛
لذلك إذا كنت تريد أن تكون الخاصية الحالية لكائن ref الذي تم إنشاؤه قابلة للتعديل، فأنت بحاجة إلى إضافة | null
.
const ref = useRef<string |.null>(null) ref.current = '' // حسنًا،
عند استدعاء أسلوب React.createRef()
، يتم إرجاع RefObject
أيضًا.
createRef
createRef(): RefObject { كائن مرجعي ثابت = { الحالي: لاغية، }; إذا (__DEV__) { Object.seal(refObject); } إرجاع refObject; }
تمت إضافة RefObject/MutableRefObject
في الإصدار 16.3
. إذا كنت تستخدم إصدارات سابقة، فستحتاج إلى استخدام Ref Callback
.
باستخدام Ref Callback
هو تمرير وظيفة رد الاتصال عند رد الاتصال، سيتم إعادة المثيل المقابل، ويمكن حفظه بنفسه للاتصال. نوع وظيفة رد الاتصال هذه هو RefCallback
.
اكتب RefCallback<T> = (المثيل: T | null) => void؛
مثال لاستخدام RefCallback
:
استيراد React من 'react'؛ فئة التصدير CustomTextInput تمتد React.Component { textInput: HTMLInputElement |.null = null; saveInputRef = (العنصر: HTMLInputElement | null) => { this.textInput = element; } يجعل() { يعود ( <input type="text" ref={this.saveInputRef} /> ); } }
في تعريف النوع، توجد أيضًا أنواع Ref/LegacyRef، والتي تُستخدم للإشارة إلى نوع Ref بشكل عام. LegacyRef
هو إصدار متوافق في الإصدار القديم السابق، وقد يكون ref
أيضًا عبارة عن سلسلة.
اكتب Ref<T> = RefCallback<T> |. اكتب LegacyRef<T> = string | Ref<T>;
فقط من خلال فهم الأنواع المرتبطة بالمرجع، يمكنك أن تصبح أكثر راحة في كتابة Typescript.
تمريرعند استخدام ref
على مكون JSX، نقوم بتعيين Ref
للسمة ref
. نعلم جميعًا أنه سيتم تجميع صيغة jsx
في شكل createElement
بواسطة أدوات مثل Babel.
//jsx <التطبيق ref={ref} id="my-app" </App> // تم تجميعها ل React.createElement(التطبيق، { المرجع: المرجع، المعرف: "تطبيقي" });
يبدو أن ref
لا يختلف عن الخاصيات الأخرى، ولكن إذا حاولت طباعةprops.ref داخل المكون، فهو undefined
. وسوف تعطي وحدة التحكم في بيئة dev
المطالبات.
ستؤدي محاولة الوصول إليها إلى إرجاع قيمة
undefined
إذا كنت تريد الوصول إلى نفس القيمة داخل المكون الفرعي، فيجب عليك تمريرها كخاصية مختلفة.
ماذا تفعل React بالمرجع؟ كما ترون في كود مصدر ReactElement، ref
هو RESERVED_PROPS
key
أيضًا هذه المعالجة، وستتم معالجتها بشكل خاص واستخراجها من الخاصيات وتمريرها إلى Element
.
ثابت RESERVED_PROPS = { المفتاح: صحيح، المرجع: صحيح، __النفس: صحيح، __المصدر : صحيح };
لذا فإن ref
هو “props“
سيتم التعامل معها بشكل خاص.
قبل الإصدار 16.8.0
، كان مكون الوظيفة عديم الحالة وسيتم عرضه فقط بناءً على الخاصيات الواردة. باستخدام الخطاف، لا يمكنك الحصول على حالة داخلية فحسب، بل يمكنك أيضًا الكشف عن طرق للاستدعاءات الخارجية (تتطلب forwardRef
و useImperativeHandle
).
إذا كنت تستخدم ref
مباشرة Function Component
، فستحذرك وحدة التحكم في بيئة التطوير من أنك بحاجة إلى تغليفه بـ forwardRef
.
وظيفة الإدخال () { العودة <الإدخال /> } مرجع ثابت = useRef() <Input ref={ref} />
لا يمكن إعطاء مراجع لمكونات الدالة. ستفشل محاولات الوصول إلى هذا المرجع. هل تقصد استخدام React.forwardRef()
forwardRef
اعرض الكود المصدري ReactForwardRef.js وقم بطي الكود ذي الصلة __DEV__
وهو مجرد مكون عالي الترتيب بسيط للغاية. احصل على FunctionComponent المعروض، وقم بتغليفه وتعريف $$typeof
كـ REACT_FORWARD_REF_TYPE
، ثم return
.
تتبع الكود وابحث عن ResolveLazyComponentTag، حيث سيتم تحليل $$typeof
في علامة العمل المقابلة.
علامة العمل المقابلة لـ REACT_FORWARD_REF_TYPE
هي ForwardRef. ثم سوف يقوم ForwardRef بإدخال منطق updateForwardRef.
مرجع الحالة إلى الأمام: { الطفل = التحديثForwardRef( باطل، العمل في التقدم, عنصر، الدعائم التي تم حلها, تقديم الممرات, ); عودة الطفل؛ }
ستستدعي هذه الطريقة طريقة renderWithHooks وتمرير ref
في المعلمة الخامسة.
nextChildren = renderWithHooks ( حاضِر، العمل في التقدم, يجعل، الدعائم التالية, المرجع، // هنا renderLanes، );
استمر في تتبع الكود وأدخل طريقة renderWithHooks. يمكنك أن ترى أنه سيتم تمرير ref
كمعلمة ثانية Component
. في هذه المرحلة يمكننا أن نفهم من أين تأتي المعلمة الثانية ref
الخاصة بـ FuncitonComponent
المغلفة بواسطة forwardRef
(مقارنة بالمعلمة الثانية لمنشئ ClassComponent وهو السياق).
بعد معرفة كيفية تمرير المرجع، السؤال التالي هو كيفية تعيين المرجع.
(قم بتعيين RefCallback للمرجع وكسر النقطة في رد الاتصال). تتبع التعليمات البرمجية التزامAttachRef. في هذه الطريقة، سيتم الحكم على ما إذا كان مرجع عقدة الألياف هو function
أو RefObject، والمثيل. سيتم معالجتها وفقا للنوع. إذا كانت العقدة الليفية عبارة عن HostComponent ( tag = 5
)، وهي عقدة DOM، فإن المثيل هو عقدة DOM؛ وإذا كانت العقدة الليفية عبارة عن ClassComponent ( tag = 1
)، فإن المثيل هو مثيل الكائن.
وظيفة الالتزامAttachRef(finishedWork) { var ref = FinishedWork.ref; إذا (المرجع !== فارغ) { var exampleToUse = FinishWork.stateNode; إذا (نوع المرجع === 'وظيفة') { ref(instanceToUse); } آخر { ref.current = exampleToUse; } } }
ما ورد أعلاه هو منطق تعيين المرجع في HostComponent وClassComponent، بالنسبة للمكونات من النوع ForwardRef، يتم استخدام رموز مختلفة، ولكن السلوك هو نفسه في الأساس. يمكنك رؤية التأثير الحتمي هنا.
بعد ذلك، نواصل البحث في كود مصدر React لمعرفة كيفية تنفيذ useRef.
يحددرمز وقت تشغيل useRef ReactFiberHooks عن طريق تتبع الكود
توجد طريقتان هنا، mountRef
و updateRef
، كما يوحي الاسم، فإنهما تتوافقان مع العمليات التي تتم على ref
عند mount
عقدة Fiber
update
.
تحديث الوظيفةRef<T>(القيمة الأولية: T): {|الحالي: T|} { ربط const = updateWorkInProgressHook(); إرجاع هوك.memoizedState; } الدالة mountRef<T>(القيمة الأولية: T): {|الحالي: T|} { ربط const = mountWorkInProgressHook(); const ref = {current: originalValue}; Hook.memoizedState = ref; مرجع العودة؛ }
يمكنك أن ترى أنه عند mount
، يقوم useRef
بإنشاء RefObject
وتخصيصه إلى hook
memoizedState
، عند update
، يتم إزالته وإعادته مباشرة.
يحفظ الخطاف memoizedState محتويات مختلفة، يحفظ useState
معلومات state
، ويحفظ useEffect
كائنات effect
، ويحفظ useRef
كائنات ref
...
يتم دعم طريقتي mountWorkInProgressHook
و updateWorkInProgressHook
بقائمة مرتبطة من الخطافات قم باسترداد نفس كائن memoizedState في كل مرة تقوم فيها بتقديم useRef. الأمر بهذه البساطة.
في هذه المرحلة، نحن نفهم منطق تمرير وتعيين ref
في React، بالإضافة إلى الكود المصدري المتعلق بـ useRef
. استخدم سؤال تطبيق لدمج نقاط المعرفة المذكورة أعلاه: يوجد مكون إدخال داخل المكون، ويجب استخدام InternalRef HTMLInputElement
للوصول إلى عقدة DOM
لتنفيذه؟
إدخال ثابت = ForwardRef((props, ref) => { const insideRef = useRef<HTMLInputElement>(خالية) يعود ( <input {...props} ref={???} /> ) })
فكر في كيفية كتابة ???
في الكود أعلاه.
============ السطر الفاصل للإجابة =========
من خلال فهم التنفيذ الداخلي المتعلق بالمرجع، من الواضح أنه يمكننا إنشاء RefCallback
هنا، والذي يمكن التعامل مع متعددة فقط قم بتعيين ref
.
دالة التصدير CombineRefs<T = Any>( refs: Array<MutableRefObject<T | null> | ): React.RefCallback<T> { قيمة الإرجاع => { refs.forEach(ref => { إذا (نوع المرجع === 'وظيفة') { المرجع(القيمة); } وإلا إذا (المرجع !== فارغة) { المرجع الحالي = القيمة؛ } }); }; } إدخال ثابت = ForwardRef((props, ref) => { const insideRef = useRef<HTMLInputElement>(خالية) يعود ( <input {...props} ref={combineRefs(ref, insideRef)} /> ) })