المزامنة مع التأثيرات
تحتاج بعض المكونات إلى المزامنة مع أنظمة خارجية. على سبيل المثال، قد ترغب في التحكم بمكون غير React بناءً على حالة React، أو إعداد اتصال بالخادم، أو إرسال سجل تحليلات عندما يظهر المكون على الشاشة.التأثيراتتتيح لك تشغيل بعض التعليمات البرمجية بعد التصيير حتى تتمكن من مزامنة مكونك مع نظام خارج React.
سوف تتعلم
- ما هي التأثيرات
- كيف تختلف التأثيرات عن الأحداث
- كيف تعلن عن تأثير في مكونك
- كيف تتخطى إعادة تشغيل تأثير بشكل غير ضروري
- لماذا تعمل التأثيرات مرتين في بيئة التطوير وكيف تصلحها
ما هي التأثيرات وكيف تختلف عن الأحداث؟
قبل التعمق في التأثيرات، يجب أن تكون على دراية بنوعين من المنطق داخل مكونات React:
- كود التصيير(تم تقديمه فيوصف واجهة المستخدم) يوجد في المستوى الأعلى لمكونك. هذا هو المكان الذي تأخذ فيه الخصائص والحالة، وتحولها، وتعيد JSX الذي تريد رؤيته على الشاشة.يجب أن يكون كود التصيير نقيًا.مثل معادلة رياضية، يجب أنتحسبالنتيجة فقط، دون القيام بأي شيء آخر.
- معالجات الأحداث(تم تقديمها فيإضافة التفاعلية) هي دوال متداخلة داخل مكوناتكتفعلأشياء بدلاً من مجرد حسابها. قد يقوم معالج حدث بتحديث حقل إدخال، أو إرسال طلب HTTP POST لشراء منتج، أو توجيه المستخدم إلى شاشة أخرى. تحتوي معالجات الأحداث على"آثار جانبية"(فهي تغير حالة البرنامج) ناتجة عن إجراء مستخدم محدد (على سبيل المثال، نقرة زر أو كتابة).
أحيانًا لا يكون هذا كافيًا. فكر في مكونChatRoomالذي يجب أن يتصل بخادم الدردشة كلما كان مرئيًا على الشاشة. الاتصال بخادم ليس عملية حسابية نقية (إنه أثر جانبي) لذا لا يمكن أن يحدث أثناء التصيير. ومع ذلك، لا يوجد حدث معين مثل نقرة يتسبب في عرضChatRoom.
التأثيراتتتيح لك تحديد الآثار الجانبية الناتجة عن عملية التصيير نفسها، وليس عن حدث معين.إرسال رسالة في الدردشة هوحدثلأنه ناتج مباشرة عن قيام المستخدم بالنقر على زر محدد. ومع ذلك، فإن إعداد اتصال بالخادم هوتأثيرلأنه يجب أن يحدث بغض النظر عن أي تفاعل تسبب في ظهور المكون. تعمل التأثيرات في نهاية عمليةالإيداعبعد تحديث الشاشة. هذا هو الوقت المناسب لمزامنة مكونات React مع نظام خارجي (مثل الشبكة أو مكتبة طرف ثالث).
ملاحظة
هنا وفي النص لاحقًا، تشير كلمة "تأثير" (Effect) بحرف كبير إلى التعريف الخاص بـ React المذكور أعلاه، أي أثر جانبي ناتج عن التصيير. للإشارة إلى المفهوم البرمجي الأوسع، سنقول "أثر جانبي".
قد لا تحتاج إلى تأثير
لا تتعجل في إضافة تأثيرات إلى مكوناتك.تذكر أن التأثيرات تُستخدم عادةً "للخروج" من كود React الخاص بك والمزامنة مع بعض الأنظمةالخارجية. وهذا يشمل واجهات برمجة تطبيقات المتصفح، وأدوات الطرف الثالث، والشبكة، وما إلى ذلك. إذا كان تأثيرك يعدل فقط بعض الحالة بناءً على حالة أخرى،فقد لا تحتاج إلى تأثير.
كيفية كتابة تأثير
لكتابة تأثير، اتبع هذه الخطوات الثلاث:
- أعلن عن تأثير.بشكل افتراضي، سيعمل تأثيرك بعد كل عمليةإيداع.
- حدد تبعيات التأثير.يجب أن تعمل معظم التأثيرات مرة أخرى فقطعند الحاجةوليس بعد كل عملية تصيير. على سبيل المثال، يجب أن يتم تشغيل رسوم متحركة للظهور التدريجي فقط عندما يظهر المكون. يجب أن يحدث الاتصال بغرفة دردشة والانفصال عنها فقط عندما يظهر المكون ويختفي، أو عندما تتغير غرفة الدردشة. ستتعلم كيفية التحكم في ذلك عن طريق تحديدالتبعيات.
- أضف تنظيفًا إذا لزم الأمر.تحتاج بعض التأثيرات إلى تحديد كيفية إيقاف أو التراجع عن أو تنظيف أي شيء كانت تقوم به. على سبيل المثال، يحتاج "الاتصال" إلى "الانفصال"، ويحتاج "الاشتراك" إلى "إلغاء الاشتراك"، ويحتاج "جلب البيانات" إما إلى "إلغاء" أو "تجاهل". ستتعلم كيفية القيام بذلك عن طريق إرجاعدالة تنظيف.
لنلقِ نظرة على كل من هذه الخطوات بالتفصيل.
الخطوة 1: أعلن عن تأثير
لإعلان تأثير في مكونك، استوردخطاف useEffectمن React:
ثم، استدعِها في المستوى الأعلى لمكونك وضع بعض التعليمات البرمجية داخل التأثير الخاص بك:
في كل مرة يتم فيها عرض مكونك، سيقوم React بتحديث الشاشةثمتشغيل التعليمات البرمجية داخلuseEffect. بعبارة أخرى،useEffect"يؤجل" تشغيل جزء من التعليمات البرمجية حتى ينعكس ذلك العرض على الشاشة.
دعنا نرى كيف يمكنك استخدام تأثير للمزامنة مع نظام خارجي. فكر في مكون React<VideoPlayer>. سيكون من الجيد التحكم فيما إذا كان يعمل أو متوقف مؤقتًا عن طريق تمرير خاصيةisPlayingإليه:
مكونك المخصصVideoPlayerيعرض وسم المتصفح المدمج<video>:
ومع ذلك، فإن وسم المتصفح<video>ليس لديه خاصيةisPlaying. الطريقة الوحيدة للتحكم فيه هي استدعاء الدوالplay() و pause()يدويًا على عنصر DOM.أنت بحاجة إلى مزامنة قيمة الخاصيةisPlaying، التي تخبر ما إذا كان الفيديويجبأن يعمل حاليًا، مع استدعاءات مثلplay() و pause().
سنحتاج أولاً إلىالحصول على مرجعلعقدة DOM الخاصة بـ<video>.
قد تميل إلى محاولة استدعاءplay()أوpause()أثناء العرض، ولكن هذا غير صحيح:
سبب عدم صحة هذا الكود هو أنه يحاول القيام بشيء ما مع عقدة DOM أثناء التصيير. في React، يجب أن يكونالتصيير عملية حسابية نقيةلـ JSX ولا يجب أن يحتوي على تأثيرات جانبية مثل تعديل DOM.
علاوة على ذلك، عندما يتم استدعاءVideoPlayerلأول مرة، فإن DOM الخاص به غير موجود بعد! لا توجد عقدة DOM بعد لاستدعاءplay()أوpause()عليها، لأن React لا تعرف أي DOM يجب إنشاؤه حتى تقوم بإرجاع JSX.
الحل هنا هوتغليف التأثير الجانبي باستخدامuseEffectلنقله خارج عملية حساب التصيير:
عن طريق تغليف تحديث DOM في Effect، تسمح لـ React بتحديث الشاشة أولاً. ثم يتم تشغيل Effect الخاص بك.
عندما يتم تصيير مكونVideoPlayerالخاص بك (سواء للمرة الأولى أو إذا أعيد تصييره)، ستحدث بعض الأشياء. أولاً، سيقوم React بتحديث الشاشة، مما يضمن أن علامة<video>موجودة في DOM مع الخصائص الصحيحة. ثم سيقوم React بتشغيل Effect الخاص بك. أخيرًا، سيقوم Effect الخاص بك باستدعاءplay()أوpause()اعتمادًا على قيمةisPlaying.
اضغط على Play/Pause عدة مرات وشاهد كيف يظل مشغل الفيديو متزامنًا مع قيمةisPlaying:
في هذا المثال، كان "النظام الخارجي" الذي قمت بمزامنته مع حالة React هو واجهة برمجة تطبيقات الوسائط في المتصفح. يمكنك استخدام نهج مشابه لتغليف كود غير React قديم (مثل إضافات jQuery) في مكونات React تصريحية.
لاحظ أن التحكم في مشغل الفيديو أكثر تعقيدًا بكثير في الممارسة العملية. استدعاءplay()قد يفشل، وقد يقوم المستخدم بتشغيل أو إيقاف الفيديو باستخدام عناصر التحكم المدمجة في المتصفح، وهكذا. هذا المثال مبسط جدًا وغير مكتمل.
مأزق
بشكل افتراضي، تعمل التأثيرات بعدكلعملية عرض. هذا هو السبب في أن كودًا مثل هذا سيؤدي إلىحلقة لا نهائية:
تعمل التأثيرات كنتيجةلعملية العرض. تعيين الحالةيُطلقعملية العرض. تعيين الحالة فورًا داخل تأثير يشبه توصيل مقبس طاقة بنفسه. يعمل التأثير، يعين الحالة، مما يتسبب في إعادة العرض، مما يتسبب في عمل التأثير، يعين الحالة مرة أخرى، وهذا يتسبب في إعادة عرض أخرى، وهكذا.
يجب أن تقوم التأثيرات عادة بمزامنة مكوناتك مع نظامخارجي. إذا لم يكن هناك نظام خارجي وتريد فقط ضبط بعض الحالة بناءً على حالة أخرى،قد لا تحتاج إلى تأثير.
الخطوة 2: تحديد تبعيات التأثير
بشكل افتراضي، تعمل التأثيرات بعدكلعملية عرض. غالبًا، هذاليس ما تريده:
- في بعض الأحيان، يكون بطيئًا. المزامنة مع نظام خارجي ليست فورية دائمًا، لذا قد ترغب في تخطي القيام بها إلا إذا كانت ضرورية. على سبيل المثال، لا تريد إعادة الاتصال بخادم الدردشة مع كل ضغطة مفتاح.
- في بعض الأحيان، يكون خاطئًا. على سبيل المثال، لا تريد تشغيل تأثير تحريك ظهور تدريجي للمكون مع كل ضغطة مفتاح. يجب أن يعمل التحريك مرة واحدة فقط عندما يظهر المكون لأول مرة.
لتوضيح المشكلة، إليك المثال السابق مع بعض استدعاءاتconsole.logوحقل إدخال نصي يقوم بتحديث حالة المكون الأب. لاحظ كيف يؤدي الكتابة إلى إعادة تشغيل التأثير:
يمكنك إخبار Reactبتخطي إعادة تشغيل التأثير بشكل غير ضروريعن طريق تحديد مصفوفة منالتبعياتكوسيط ثاني لاستدعاءuseEffect. ابدأ بإضافة مصفوفة فارغة[]إلى المثال أعلاه في السطر 14:
يجب أن ترى خطأ يقولReact Hook useEffect has a missing dependency: 'isPlaying':
المشكلة هي أن الكود داخل التأثير الخاص بكيعتمد علىالخاصيةisPlayingليقرر ما يجب فعله، لكن هذه التبعية لم تُصرح بها بشكل صريح. لإصلاح هذه المشكلة، أضفisPlayingإلى مصفوفة التبعيات:
الآن تم الإعلان عن جميع التبعيات، لذلك لا يوجد خطأ. تحديد[isPlaying]كمصفوفة التبعيات يخبر React أنه يجب عليها تخطي إعادة تشغيل التأثير الخاص بك إذا كانت قيمةisPlayingهي نفسها كما كانت أثناء التصيير السابق. مع هذا التغيير، الكتابة في حقل الإدخال لا تتسبب في إعادة تشغيل التأثير، لكن الضغط على زر التشغيل/الإيقاف المؤقت يفعل ذلك:
يمكن أن تحتوي مصفوفة الاعتماديات على عدة اعتماديات. سيتخطى React إعادة تشغيل التأثير فقط إذا كانتجميعالاعتماديات التي تحددها لها نفس القيم تمامًا كما كانت أثناء التصيير السابق. يقارن React قيم الاعتماديات باستخدام مقارنةObject.is. راجع مرجع useEffectللحصول على التفاصيل.
لاحظ أنه لا يمكنك "اختيار" اعتمادياتك.ستحصل على خطأ من أداة التحقق (lint) إذا كانت الاعتماديات التي حددتها لا تتطابق مع ما يتوقعه React بناءً على الكود داخل تأثيرك. يساعد هذا في اكتشاف العديد من الأخطاء في الكود الخاص بك. إذا كنت لا تريد إعادة تشغيل بعض الأجزاء من الكود،قم بتعديل كود التأثير نفسه حتى لا "يحتاج" إلى تلك الاعتمادية.
مشكلة محتملة
السلوكيات بدون مصفوفة الاعتماديات ومع مصفوفة اعتمادياتفارغة[] مختلفة:
سنلقي نظرة فاحصة على معنى "mount" في الخطوة التالية.
الخطوة 3: أضف تنظيفًا إذا لزم الأمر
فكر في مثال مختلف. أنت تكتب مكونChatRoomيحتاج إلى الاتصال بخادم الدردشة عند ظهوره. لديك واجهة برمجة تطبيقاتcreateConnection()تُرجع كائنًا يحتوي على طرقconnect() و disconnect(). كيف تحافظ على اتصال المكون أثناء عرضه للمستخدم؟
ابدأ بكتابة منطق التأثير:
سيكون الاتصال بالدردشة بعد كل إعادة تصيير بطيئًا، لذا تضيف مصفوفة الاعتماديات:
الكود داخل التأثير لا يستخدم أي خاصيات (props) أو حالة (state)، لذا فإن مصفوفة الاعتماديات الخاصة بك هي[](فارغة). هذا يخبر React بتشغيل هذا الكود فقط عندما "يتم تركيب" المكون، أي يظهر على الشاشة لأول مرة.
لنجرب تشغيل هذا الكود:
يتم تشغيل هذا التأثير فقط عند التثبيت، لذا قد تتوقع ظهور"✅ Connecting..."مرة واحدة في وحدة التحكم.ولكن إذا قمت بفحص وحدة التحكم، ستجد أن"✅ Connecting..."يُطبع مرتين. لماذا يحدث هذا؟
تخيل أن مكونChatRoomهو جزء من تطبيق أكبر يحتوي على العديد من الشاشات المختلفة. يبدأ المستخدم رحلته على صفحةChatRoom. يتم تثبيت المكون واستدعاءconnection.connect(). ثم تخيل أن المستخدم ينتقل إلى شاشة أخرى - على سبيل المثال، إلى صفحة الإعدادات. يتم إلغاء تثبيت مكونChatRoom. أخيرًا، ينقر المستخدم على زر الرجوع ويتم تثبيتChatRoomمرة أخرى. هذا سينشئ اتصالًا ثانيًا - لكن الاتصال الأول لم يُدمر أبدًا! بينما يتنقل المستخدم عبر التطبيق، ستستمر الاتصالات في التراكم.
من السهل تفويت الأخطاء مثل هذه دون اختبار يدوي مكثف. لمساعدتك في اكتشافها بسرعة، في وضع التطوير، يقوم React بإعادة تثبيت كل مكون مرة واحدة مباشرة بعد تثبيته الأولي.
رؤية سجل"✅ Connecting..."مرتين يساعدك على ملاحظة المشكلة الحقيقية: كودك لا يُغلق الاتصال عندما يتم إلغاء تثبيت المكون.
لإصلاح المشكلة، قم بإرجاعدالة تنظيفمن التأثير الخاص بك:
سيستدعي React دالة التنظيف الخاصة بك في كل مرة قبل تشغيل التأثير مرة أخرى، ومرة أخيرة عندما يتم إلغاء تثبيت المكون (إزالته). دعنا نرى ما يحدث عند تنفيذ دالة التنظيف:
الآن تحصل على ثلاثة سجلات في وحدة التحكم في وضع التطوير:
"✅ Connecting...""❌ Disconnected.""✅ Connecting..."
هذا هو السلوك الصحيح في وضع التطوير.من خلال إعادة تثبيت المكون الخاص بك، يتحقق React من أن الانتقال بعيدًا والعودة لن يكسر كودك. قطع الاتصال ثم الاتصال مرة أخرى هو بالضبط ما يجب أن يحدث! عندما تنفذ التنظيف بشكل جيد، لا يجب أن يكون هناك فرق مرئي للمستخدم بين تشغيل التأثير مرة واحدة مقابل تشغيله، تنظيفه، ثم تشغيله مرة أخرى. هناك زوج إضافي من استدعاءات الاتصال/قطع الاتصال لأن React يفحص كودك بحثًا عن أخطاء في وضع التطوير. هذا طبيعي - لا تحاول التخلص منه!
في الإنتاج، سترى فقط"✅ Connecting..."مطبوعًا مرة واحدة.تحدث إعادة تثبيت المكونات فقط في وضع التطوير لمساعدتك في العثور على التأثيرات التي تحتاج إلى تنظيف. يمكنك إيقاف تشغيلالوضع الصارمللانسحاب من سلوك التطوير، لكننا نوصي بالاحتفاظ به قيد التشغيل. هذا يتيح لك العثور على العديد من الأخطاء مثل المذكور أعلاه.
كيفية التعامل مع تشغيل التأثير مرتين في التطوير؟
يقوم React عمدًا بإعادة تثبيت مكوناتك في وضع التطوير للعثور على أخطاء مثل المثال الأخير.السؤال الصحيح ليس "كيف أجعل التأثير يعمل مرة واحدة"، بل "كيف أصلح تأثري بحيث يعمل بعد إعادة التثبيت".
عادةً، الإجابة هي تنفيذ دالة التنظيف. يجب أن توقف دالة التنظيف أو تتراجع عن أي شيء كان التأثير يفعله. القاعدة الأساسية هي أن المستخدم لا يجب أن يكون قادرًا على التمييز بين تشغيل التأثير مرة واحدة (كما في الإنتاج) وتسلسلإعداد → تنظيف → إعداد(كما ستراه في التطوير).
معظم التأثيرات التي ستكتبها ستندرج تحت أحد الأنماط الشائعة أدناه.
مأزق
لا تستخدم المراجع لمنع تشغيل التأثيرات
مأزق شائع لمنع تشغيل التأثيرات مرتين في التطوير هو استخدامrefلمنع التأثير من التشغيل أكثر من مرة. على سبيل المثال، يمكنك "إصلاح" الخطأ أعلاه باستخدامuseRef:
هذا يجعل الأمر بحيث ترى"✅ Connecting..."مرة واحدة فقط في التطوير، لكنه لا يصلح الخطأ.
عندما ينتقل المستخدم بعيدًا، لا يزال الاتصال غير مغلق وعندما يعود، يتم إنشاء اتصال جديد. بينما يتنقل المستخدم عبر التطبيق، ستستمر الاتصالات في التراكم، تمامًا كما كان قبل "الإصلاح".
لإصلاح الخطأ، لا يكفي مجرد جعل التأثير يعمل مرة واحدة. يحتاج التأثير إلى العمل بعد إعادة التثبيت، مما يعني أن الاتصال يحتاج إلى التنظيف كما في الحل أعلاه.
انظر الأمثلة أدناه لمعرفة كيفية التعامل مع الأنماط الشائعة.
التحكم في عناصر واجهة المستخدم غير React
في بعض الأحيان تحتاج إلى إضافة عناصر واجهة مستخدم لم تُكتب باستخدام React. على سبيل المثال، لنفترض أنك تضيف مكون خريطة إلى صفحتك. لديه طريقة
لاحظ أنه لا حاجة لتنظيف في هذه الحالة. في بيئة التطوير، سيقوم React باستدعاء Effect مرتين، لكن هذا ليس مشكلة لأن استدعاءsetZoomLevelمرتين بنفس القيمة لا يفعل شيئًا. قد يكون أبطأ قليلاً، لكن هذا لا يهم لأنه لن يعيد التثبيت دون داعٍ في بيئة الإنتاج.
بعض واجهات البرمجة قد لا تسمح لك باستدعائها مرتين متتاليتين. على سبيل المثال، طريقةshowModalالخاصة بعنصر<dialog>المدمج ترمي خطأ إذا استدعيتها مرتين. نفّذ دالة التنظيف واجعلها تغلق الحوار:
في بيئة التطوير، سيقوم Effect الخاص بك باستدعاءshowModal()، ثم close()فورًا، ثمshowModal()مرة أخرى. هذا له نفس السلوك المرئي للمستخدم كاستدعاءshowModal()مرة واحدة، كما ستراه في بيئة الإنتاج.
الاشتراك في الأحداث
إذا كان Effect الخاص بك يشترك في شيء ما، فيجب أن تقوم دالة التنظيف بإلغاء الاشتراك:
في بيئة التطوير، سيقوم Effect الخاص بك باستدعاءaddEventListener()، ثم removeEventListener()فورًا، ثمaddEventListener()مرة أخرى بنفس المعالج. لذا سيكون هناك اشتراك نشط واحد فقط في كل مرة. هذا له نفس السلوك المرئي للمستخدم كاستدعاءaddEventListener()مرة واحدة، كما في بيئة الإنتاج.
تشغيل الرسوم المتحركة
إذا كان Effect الخاص بك يُحرّك شيئًا ما، فيجب أن تعيد دالة التنظيف الرسوم المتحركة إلى قيمها الأولية:
في بيئة التطوير، سيتم تعيين العتامة إلى1، ثم إلى0، ثم إلى1مرة أخرى. يجب أن يكون لهذا نفس السلوك المرئي للمستخدم كتعيينها إلى1مباشرة، وهو ما سيحدث في بيئة الإنتاج. إذا كنت تستخدم مكتبة رسوم متحركة تابعة لجهة خارجية تدعم التلطيف (tweening)، فيجب أن تعيد دالة التنظيف الجدول الزمني إلى حالته الأولية.
جلب البيانات
إذا كان تأثيرك يجلب شيئًا ما، فيجب أن تقوم دالة التنظيف إمابإلغاء عملية الجلبأو تجاهل نتيجته:
لا يمكنك "تراجع" عن طلب شبكة حدث بالفعل، ولكن يجب أن تضمن دالة التنظيف أن عملية الجلب التيلم تعد ذات صلةلا تستمر في التأثير على تطبيقك. إذا تغيرuserId من 'Alice'إلى'Bob'، فإن التنظيف يضمن تجاهل استجابة'Alice'حتى لو وصلت بعد'Bob'.
في بيئة التطوير، سترى عمليتي جلب في علامة تبويب الشبكة.لا يوجد خطأ في ذلك. باستخدام النهج أعلاه، سيتم تنظيف التأثير الأول على الفور بحيث سيتم تعيين نسخته من المتغيرignore إلى true. لذا على الرغم من وجود طلب إضافي، فإنه لن يؤثر على الحالة بفضل التحققif (!ignore).
في بيئة الإنتاج، سيكون هناك طلب واحد فقط.إذا كان الطلب الثاني في بيئة التطوير يزعجك، فإن أفضل نهج هو استخدام حل يقوم بإزالة التكرار للطلبات ويخزن استجاباتها مؤقتًا بين المكونات:
هذا لن يحسن تجربة التطوير فحسب، بل سيجعل تطبيقك يشعر بأنه أسرع. على سبيل المثال، لن يضطر المستخدم الذي يضغط على زر الرجوع إلى الانتظار حتى يتم تحميل بعض البيانات مرة أخرى لأنها ستكون مخزنة مؤقتًا. يمكنك إما بناء مثل هذا المخزن المؤقت بنفسك أو استخدام أحد البدائل العديدة للجلب اليدوي في التأثيرات.
إرسال التحليلات
ضع في اعتبارك هذا الكود الذي يرسل حدث تحليلات عند زيارة الصفحة:
في بيئة التطوير، سيتم استدعاءlogVisitمرتين لكل عنوان URL، لذا قد تميل إلى محاولة إصلاح ذلك.نوصي بالاحتفاظ بهذا الكود كما هو.كما في الأمثلة السابقة، لا يوجد فرق في السلوكالمرئي للمستخدمبين تشغيله مرة واحدة وتشغيله مرتين. من الناحية العملية، لا ينبغي أن يقومlogVisitبأي شيء في بيئة التطوير لأنك لا تريد أن تشوه السجلات من أجهزة التطوير مقاييس الإنتاج. يتم إعادة تركيب المكون الخاص بك في كل مرة تقوم فيها بحفظ ملفه، لذلك يسجل زيارات إضافية في بيئة التطوير على أي حال.
في بيئة الإنتاج، لن تكون هناك سجلات زيارات مكررة.
لتصحيح أحداث التحليلات التي ترسلها، يمكنك نشر تطبيقك في بيئة تجريبية (تعمل في وضع الإنتاج) أو الانسحاب مؤقتًا منالوضع الصارموفحوصات إعادة التركيب الخاصة بالتطوير فقط. يمكنك أيضًا إرسال التحليلات من معالجات أحداث تغيير المسار بدلاً من التأثيرات. للحصول على تحليلات أكثر دقة، يمكن أن تساعدكمراقبو التقاطعفي تتبع المكونات الموجودة في نافذة العرض والمدة التي تظل فيها مرئية.
ليس تأثيرًا: تهيئة التطبيق
يجب أن يتم تنفيذ بعض المنطق مرة واحدة فقط عند بدء تشغيل التطبيق. يمكنك وضعه خارج مكوناتك:
يضمن ذلك تنفيذ هذا المنطق مرة واحدة فقط بعد تحميل المتصفح للصفحة.
ليس تأثيرًا: شراء منتج
في بعض الأحيان، حتى إذا كتبت دالة تنظيف، لا توجد طريقة لمنع العواقب المرئية للمستخدم لتشغيل التأثير مرتين. على سبيل المثال، ربما يرسل تأثيرك طلب POST مثل شراء منتج:
لن ترغب في شراء المنتج مرتين. ومع ذلك، هذا هو السبب أيضًا في أنه لا يجب وضع هذا المنطق في تأثير. ماذا لو ذهب المستخدم إلى صفحة أخرى ثم ضغط على زر الرجوع؟ سيتم تشغيل تأثيرك مرة أخرى. لا تريد شراء المنتج عندمايزورالمستخدم صفحة؛ تريد شراءه عندماينقرالمستخدم على زر الشراء.
الشراء لا ينتج عن التصيير؛ بل ينتج عن تفاعل محدد. يجب أن يتم تنفيذه فقط عندما يضغط المستخدم على الزر.احذف التأثير وانقل طلب/api/buyالخاص بك إلى معالج حدث زر الشراء:
يوضح هذا أنه إذا أدى إعادة التثبيت إلى كسر منطق تطبيقك، فإن هذا عادةً ما يكشف عن أخطاء موجودة مسبقًا.من منظور المستخدم، زيارة صفحة ما لا ينبغي أن تختلف عن زيارتها، والنقر على رابط، ثم الضغط على زر الرجوع لعرض الصفحة مرة أخرى. يتحقق React من أن مكوناتك تلتزم بهذا المبدأ عن طريق إعادة تثبيتها مرة واحدة في بيئة التطوير.
تجميع كل شيء معًا
يمكن أن يساعدك هذا الملعب في "الشعور" بكيفية عمل التأثيرات (Effects) عمليًا.
يستخدم هذا المثالsetTimeoutلجدولة ظهور سجل في وحدة التحكم (console log) يحتوي على نص الإدخال بعد ثلاث ثوانٍ من تشغيل التأثير (Effect). تقوم دالة التنظيف (cleanup function) بإلغاء المهلة الزمنية المعلقة. ابدأ بالضغط على "Mount the component":
سترى في البداية ثلاثة سجلات:Schedule "a" log، وCancel "a" log، وSchedule "a" logمرة أخرى. وبعد ثلاث ثوانٍ سيكون هناك أيضًا سجل يقولa. كما تعلمت سابقًا، فإن زوج الجدولة/الإلغاء الإضافي موجود لأن React يعيد تثبيت المكون مرة واحدة في بيئة التطوير للتحقق من أنك قمت بتنفيذ التنظيف بشكل جيد.
الآن قم بتحرير حقل الإدخال لكتابةabc. إذا قمت بذلك بسرعة كافية، سترىSchedule "ab" logمتبوعًا مباشرة بـCancel "ab" log و Schedule "abc" log.يقوم React دائمًا بتنظيف تأثير (Effect) التصيير السابق قبل تأثير التصيير التالي.لهذا السبب، حتى إذا كتبت في حقل الإدخال بسرعة، هناك مهلة زمنية واحدة مجدولة على الأكثر في أي وقت. قم بتحرير حقل الإدخال عدة مرات وشاهد وحدة التحكم لتفهم كيف يتم تنظيف التأثيرات.
اكتب شيئًا في حقل الإدخال ثم اضغط فورًا على "Unmount the component". لاحظ كيف يؤدي إلغاء التثبيت إلى تنظيف تأثير (Effect) التصيير الأخير. هنا، يقوم بإلغاء المهلة الزمنية الأخيرة قبل أن تتاح لها فرصة التنفيذ.
أخيرًا، قم بتحرير المكون أعلاه وتعليق دالة التنظيف بحيث لا يتم إلغاء المهلات الزمنية. حاول كتابةabcdeبسرعة. ماذا تتوقع أن يحدث بعد ثلاث ثوانٍ؟ هل ستطبعconsole.log(text)داخل المهلة الزمنيةأحدثtextوتنتج خمسة سجلاتabcde؟ جرب ذلك للتحقق من حدسك!
بعد ثلاث ثوانٍ، يجب أن ترى سلسلة من السجلات (a، وab، وabc، وabcd، وabcde) بدلاً من خمسة سجلاتabcde. كل تأثير (Effect) "يُمسك" بقيمةtextمن التصيير المقابل له.لا يهم أن حالةtextقد تغيرت: تأثير من التصيير الذي يحتوي علىtext = 'ab'
ملخص
- على عكس الأحداث، تنتج التأثيرات عن عملية التصيير نفسها وليس عن تفاعل معين.
- تسمح لك التأثيرات بمزامنة المكون مع نظام خارجي (واجهة برمجة تطبيقات تابعة لجهة خارجية، شبكة، إلخ).
- بشكل افتراضي، تعمل التأثيرات بعد كل عملية تصيير (بما في ذلك التصيير الأولي).
- سيتخطى React التأثير إذا كانت جميع تبعياته لها نفس القيم كما في التصيير السابق.
- لا يمكنك "اختيار" تبعياتك. يتم تحديدها بواسطة الكود الموجود داخل التأثير.
- مصفوفة التبعيات الفارغة (
[]) تتوافق مع "تركيب" المكون، أي إضافته إلى الشاشة. - في الوضع الصارم، يقوم React بتركيب المكونات مرتين (في بيئة التطوير فقط!) لاختبار تأثيراتك تحت الضغط.
- إذا تعطل تأثيرك بسبب إعادة التركيب، فأنت بحاجة إلى تنفيذ دالة تنظيف.
- سيستدعي React دالة التنظيف الخاصة بك قبل تشغيل التأثير في المرة القادمة، وأثناء إلغاء التركيب.
Try out some challenges
Challenge 1 of 4:Focus a field on mount #
In this example, the form renders a <MyInput /> component.
Use the input’s focus() method to make MyInput automatically focus when it appears on the screen. There is already a commented out implementation, but it doesn’t quite work. Figure out why it doesn’t work, and fix it. (If you’re familiar with the autoFocus attribute, pretend that it does not exist: we are reimplementing the same functionality from scratch.)
To verify that your solution works, press “Show form” and verify that the input receives focus (becomes highlighted and the cursor is placed inside). Press “Hide form” and “Show form” again. Verify the input is highlighted again.
MyInput should only focus on mount rather than after every render. To verify that the behavior is right, press “Show form” and then repeatedly press the “Make it uppercase” checkbox. Clicking the checkbox should not focus the input above it.
