v19.2Latest

إزالة تبعيات التأثير

عندما تكتب تأثيرًا، سيتحقق المحلل اللغوي من أنك قد أدرجت كل قيمة تفاعلية (مثل الخصائص والحالة) التي يقرأها التأثير في قائمة تبعيات تأثيرك. يضمن هذا أن يبقى تأثيرك متزامنًا مع أحدث الخصائص والحالة للمكون الخاص بك. قد تتسبب التبعيات غير الضرورية في تشغيل تأثيرك كثيرًا، أو حتى في إنشاء حلقة لا نهائية. اتبع هذا الدليل لمراجعة وإزالة التبعيات غير الضرورية من تأثيراتك.

سوف تتعلم
  • كيفية إصلاح حلقات التبعية اللانهائية للتأثير
  • ما يجب فعله عندما تريد إزالة تبعية
  • كيفية قراءة قيمة من تأثيرك دون "التفاعل" معها
  • كيف ولماذا تتجنب تبعيات الكائنات والدوال
  • لماذا قمع محلل التبعيات خطير، وما يجب فعله بدلاً من ذلك

يجب أن تتطابق التبعيات مع الكود

عندما تكتب تأثيرًا، تحدد أولاً كيفيةبدء وإيقافأي شيء تريد أن يفعله تأثيرك:

ثم، إذا تركت تبعيات التأثير فارغة ([])، فسيقترح المحلل اللغوي التبعيات الصحيحة:

املأها وفقًا لما يقوله المحلل اللغوي:

التأثيرات "تتفاعل" مع القيم التفاعلية.بما أنroomIdهي قيمة تفاعلية (يمكن أن تتغير بسبب إعادة التصيير)، فإن المحلل اللغوي يتحقق من أنك قد حددتها كتبعية. إذا تلقىroomIdقيمة مختلفة، سيعيد React مزامنة تأثيرك. يضمن هذا بقاء الدردشة متصلة بالغرفة المحددة و"التفاعل" مع القائمة المنسدلة:

لإزالة تبعية، أثبت أنها ليست تبعية

لاحظ أنك لا تستطيع "اختيار" تبعيات التأثير الخاص بك. كلقيمة تفاعليةتُستخدم في كود التأثير الخاص بك يجب أن تُعلن في قائمة التبعيات. قائمة التبعيات تُحدد بواسطة الكود المحيط:

القيم التفاعليةتشمل الخصائص وجميع المتغيرات والدوال المُعلنة مباشرة داخل مكونك. بما أنroomIdهي قيمة تفاعلية، لا يمكنك إزالتها من قائمة التبعيات. لن يسمح لك المدقق بذلك:

وسيكون المدقق محقًا! بما أنroomIdقد يتغير مع مرور الوقت، فإن هذا سيُدخل خطأً في كودك.

لإزالة تبعية، "أثبت" للمدقق أنهالا تحتاجإلى أن تكون تبعية.على سبيل المثال، يمكنك نقلroomIdخارج مكونك لإثبات أنها ليست تفاعلية ولن تتغير عند إعادة التصيير:

الآن بما أنroomIdليست قيمة تفاعلية (ولا يمكن أن تتغير عند إعادة التصيير)، فهي لا تحتاج إلى أن تكون تبعية:

لهذا السبب يمكنك الآن تحديدقائمة تبعيات فارغة ([]).التأثير الخاص بكلا يعتمد حقًاعلى أي قيمة تفاعلية بعد الآن، لذا فهولا يحتاج حقًاإلى إعادة التشغيل عند تغيير أي من خاصيات أو حالة المكون.

لتغيير التبعيات، غيّر الكود

ربما لاحظت نمطًا في سير عملك:

  1. أولاً،تغير الكودللتأثير الخاص بك أو كيفية تعريف قيمك التفاعلية.
  2. ثم، تتبع المحلل اللغوي وتعدل التبعيات لتتوافقمع الكود الذي قمت بتغييره.
  3. إذا لم تكن راضيًا عن قائمة التبعيات،تعود إلى الخطوة الأولى(وتغير الكود مرة أخرى).

الجزء الأخير مهم.إذا كنت تريد تغيير التبعيات، غيّر الكود المحيط أولاً.يمكنك التفكير في قائمة التبعيات على أنهاقائمة بجميع القيم التفاعلية المستخدمة في كود التأثير الخاص بك.أنت لاتختارما تضعه في تلك القائمة. القائمةتصفكودك. لتغيير قائمة التبعيات، غيّر الكود.

قد يبدو هذا وكأنك تحل معادلة. قد تبدأ بهدف (على سبيل المثال، إزالة تبعية)، وتحتاج إلى "إيجاد" الكود الذي يتوافق مع هذا الهدف. لا يجد الجميع حل المعادلات ممتعًا، ويمكن قول الشيء نفسه عن كتابة التأثيرات! لحسن الحظ، هناك قائمة بالوصفات الشائعة التي يمكنك تجربتها أدناه.

مأزق

إذا كان لديك قاعدة كود موجودة، فقد يكون لديك بعض التأثيرات التي تقمع المحلل اللغوي هكذا:

عندما لا تتطابق التبعيات مع الكود، هناك خطر كبير جدًا لإدخال أخطاء.من خلال قمع المحلل اللغوي، أنت "تكذب" على React بشأن القيم التي يعتمد عليها التأثير الخاص بك.

بدلاً من ذلك، استخدم التقنيات أدناه.

إزالة التبعيات غير الضرورية

في كل مرة تقوم فيها بضبط تبعيات التأثير لتعكس الكود، انظر إلى قائمة التبعيات. هل من المنطقي أن يعيد التأثير التشغيل عند تغيير أي من هذه التبعيات؟ في بعض الأحيان، تكون الإجابة "لا":

  • قد ترغب في إعادة تنفيذأجزاء مختلفةمن التأثير الخاص بك تحت ظروف مختلفة.
  • قد ترغب في قراءةالقيمة الأحدث فقطلبعض التبعيات بدلاً من "الاستجابة" لتغيراتها.
  • قد تتغير تبعية ما كثيرًادون قصدلأنها كائن أو دالة.

لإيجاد الحل المناسب، ستحتاج للإجابة على بعض الأسئلة حول التأثير الخاص بك. دعنا نستعرضها.

هل يجب نقل هذا الرمز إلى معالج حدث؟

أول شيء يجب أن تفكر فيه هو ما إذا كان هذا الرمز يجب أن يكون تأثيرًا من الأساس.

تخيل نموذجًا. عند الإرسال، تقوم بتعيين متغير الحالةsubmitted إلى true. تحتاج إلى إرسال طلب POST وعرض إشعار. لقد وضعت هذا المنطق داخل تأثير "يستجيب" لكونsubmitted true:

لاحقًا، تريد تنسيق رسالة الإشعار وفقًا للسمة الحالية، لذا تقرأ السمة الحالية. بما أنthemeمُعلن عنه في جسم المكون، فهو قيمة تفاعلية، لذا تضيفه كتبعية:

بفعل ذلك، أدخلت خطأ. تخيل أنك ترسل النموذج أولاً ثم تتبدل بين السمات الداكنة والفاتحة. ستتغيرtheme، وسيعيد التأثير التشغيل، وبالتالي سيعرض نفس الإشعار مرة أخرى!

المشكلة هنا هي أن هذا لم يكن يجب أن يكون تأثيرًا من الأساس.أنت تريد إرسال طلب POST هذا وعرض الإشعار استجابةًلإرسال النموذج،وهي تفاعل محدد. لتشغيل بعض التعليمات البرمجية استجابةً لتفاعل محدد، ضع هذا المنطق مباشرةً في معالج الحدث المقابل:

الآن بما أن الرمز موجود في معالج حدث، فهو غير تفاعلي—لذا سيعمل فقط عندما يرسل المستخدم النموذج. اقرأ المزيد عنالاختيار بين معالجات الأحداث والتأثيرات و كيفية حذف التأثيرات غير الضرورية.

هل يقوم تأثيرك بعدة أشياء غير مرتبطة؟

السؤال التالي الذي يجب أن تطرحه على نفسك هو ما إذا كان تأثيرك يقوم بعدة أشياء غير مرتبطة.

تخيل أنك تنشئ نموذج شحن حيث يحتاج المستخدم إلى اختيار مدينته ومنطقته. تجلب قائمةcitiesمن الخادم وفقًا للـcountryالمحدد لعرضها في قائمة منسدلة:

هذا مثال جيد علىجلب البيانات في تأثير.أنت تزامن حالةcitiesمع الشبكة وفقًا للخاصيةcountry. لا يمكنك فعل هذا في معالج حدث لأنك تحتاج إلى الجلب بمجرد عرضShippingFormوكلما تغيرتcountry(بغض النظر عن أي تفاعل يسبب ذلك).

لنفترض الآن أنك تضيف صندوق اختيار ثانٍ لمناطق المدينة، والذي يجب أن يجلبareasللمدينة المحددة حاليًاcity. قد تبدأ بإضافة استدعاءfetchثانٍ لقائمة المناطق داخل نفس التأثير:

ولكن، بما أن التأثير يستخدم الآن متغير الحالةcity، فقد اضطررت إلى إضافةcityإلى قائمة التبعيات. وهذا بدوره أدخل مشكلة: عندما يختار المستخدم مدينة مختلفة، سيعيد التأثير التشغيل وسيستدعيfetchCities(country). ونتيجة لذلك، ستقوم بإعادة جلب قائمة المدن عدة مرات دون داعٍ.

المشكلة في هذا الرمز هي أنك تقوم بمزامنة شيئين مختلفين غير مرتبطين:

  1. تريد مزامنة حالةcitiesمع الشبكة بناءً على الخاصيةcountry.
  2. تريد مزامنة حالةareasمع الشبكة بناءً على حالةcity.

قسّم المنطق إلى تأثيرين، كل منهما يستجيب للخاصية التي يحتاج إلى المزامنة معها:

الآن التأثير الأول يعيد التشغيل فقط إذا تغيرتcountry، بينما التأثير الثاني يعيد التشغيل عندما تتغيرcity. لقد فصلتهما حسب الغرض: شيئان مختلفان تتم مزامنتهما بتأثيرين منفصلين. لتأثيرين منفصلين قائمتا تبعيات منفصلتان، لذا لن يحفزا بعضهما البعض عن غير قصد.

الرمز النهائي أطول من الأصلي، لكن فصل هذه التأثيرات لا يزال صحيحًا.يجب أن يمثل كل تأثير عملية مزامنة مستقلة.في هذا المثال، حذف تأثير واحد لا يكسر منطق التأثير الآخر. هذا يعني أنهمايتم مزامنة أشياء مختلفة،ومن الجيد فصلهما. إذا كنت قلقًا بشأن التكرار، يمكنك تحسين هذا الرمز عن طريقاستخراج المنطق المتكرر في خطاف مخصص.

هل تقرأ بعض الحالة لحساب الحالة التالية؟

يحدث هذا التأثير متغير الحالةmessagesبمصفوفة جديدة تم إنشاؤها في كل مرة تصل فيها رسالة جديدة:

يستخدم المتغيرmessages لـ إنشاء مصفوفة جديدةتبدأ بجميع الرسائل الموجودة وتضيف الرسالة الجديدة في النهاية. ومع ذلك، بما أنmessagesهي قيمة تفاعلية يقرأها تأثير، يجب أن تكون تبعية:

وجعلmessagesتبعية يطرح مشكلة.

في كل مرة تستقبل رسالة، تتسببsetMessages()في إعادة عرض المكون بمصفوفةmessagesجديدة تتضمن الرسالة المستلمة. ومع ذلك، بما أن هذا التأثير يعتمد الآن علىmessages، فإن هذا سوفأيضًايعيد مزامنة التأثير. لذا فإن كل رسالة جديدة ستجعل الدردشة تعيد الاتصال. لن يحب المستخدم ذلك!

لإصلاح المشكلة، لا تقرأmessagesداخل التأثير. بدلاً من ذلك، مرردالة محدّثةإلىsetMessages:

لاحظ كيف أن التأثير الخاص بك لا يقرأ متغيرmessagesعلى الإطلاق الآن.تحتاج فقط إلى تمرير دالة تحديث مثلmsgs => [...msgs, receivedMessage]. يقوم Reactبوضع دالة التحديث الخاصة بك في قائمة انتظاروسيوفر وسيطmsgsلها أثناء التصيير التالي. هذا هو السبب في أن التأثير نفسه لم يعد بحاجة إلى الاعتماد علىmessages. نتيجة لهذا الإصلاح، لن يؤدي استلام رسالة دردشة إلى إعادة اتصال الدردشة مرة أخرى.

هل تريد قراءة قيمة دون "التفاعل" مع تغيراتها؟

لنفترض أنك تريد تشغيل صوت عندما يتلقى المستخدم رسالة جديدة إلا إذا كانتisMutedهيtrue:

بما أن التأثير الخاص بك يستخدم الآنisMutedفي كوداته، يجب عليك إضافته إلى التبعيات:

المشكلة هي أنه في كل مرة يتغير فيهاisMuted(على سبيل المثال، عندما يضغط المستخدم على زر "كتم الصوت")، سيعيد التأثير المزامنة، وسيعيد الاتصال بالدردشة. هذه ليست تجربة المستخدم المرغوبة! (في هذا المثال، حتى تعطيل المحلل الثابت لن يعمل—إذا فعلت ذلك، فسيعلقisMutedبقيمته القديمة.)

لحل هذه المشكلة، تحتاج إلى استخراج المنطق الذي لا ينبغي أن يكون تفاعليًا من التأثير. أنت لا تريد أن "يتفاعل" هذا التأثير مع التغيرات فيisMuted.انقل هذه القطعة غير التفاعلية من المنطق إلى حدث تأثير:

تتيح لك أحداث التأثير تقسيم التأثير إلى أجزاء تفاعلية (والتي يجب أن "تتفاعل" مع القيم التفاعلية مثلroomIdوتغيراتها) وأجزاء غير تفاعلية (والتي تقرأ فقط أحدث قيمها، مثل قراءةonMessage لـ isMuted).الآن بعد أن قرأتisMutedداخل حدث تأثير، لم يعد بحاجة إلى أن يكون تبعية للتأثير الخاص بك.نتيجة لذلك، لن تعيد الدردشة الاتصال عند تبديل إعداد "كتم الصوت" تشغيلًا وإيقافًا، مما يحل المشكلة الأصلية!

تغليف معالج حدث من الخصائص

قد تواجه مشكلة مماثلة عندما يتلقى مكونك معالج حدث كخاصية:

لنفترض أن المكون الأب يمرر دالةمختلفةonReceiveMessageفي كل تصيير:

بما أنonReceiveMessageهي تبعية، فإنها ستتسبب في إعادة التأثير المزامنة بعد كل إعادة تصيير من المكون الأب. وهذا سيجعله يعيد الاتصال بالدردشة. لحل هذه المشكلة، قم بتغليف الاستدعاء في حدث تأثير:

أحداث التأثير ليست تفاعلية، لذا لا تحتاج إلى تحديدها كتبعيات. نتيجة لذلك، لن تعيد الدردشة الاتصال حتى لو مرر المكون الأب دالة مختلفة في كل إعادة تصيير.

فصل الكود التفاعلي وغير التفاعلي

في هذا المثال، تريد تسجيل زيارة في كل مرة يتغير فيهاroomId. تريد تضمين notificationCountالحالي مع كل سجل، لكنكلاتريد أن يؤدي تغييرnotificationCountإلى تشغيل حدث تسجيل.

الحل مرة أخرى هو فصل الكود غير التفاعلي إلى حدث تأثير:

تريد أن يكون منطقك تفاعليًا فيما يتعلق بـroomId، لذا تقرأroomIdداخل التأثير الخاص بك. ومع ذلك، لا تريد أن يؤدي تغييرnotificationCountإلى تسجيل زيارة إضافية، لذا تقرأnotificationCountداخل حدث التأثير.تعرف على المزيد حول قراءة أحدث الخصائص والحالة من التأثيرات باستخدام أحداث التأثير.

هل تتغير بعض القيمة التفاعلية بشكل غير مقصود؟

في بعض الأحيان،تريدبالفعل أن "يتفاعل" تأثيرك مع قيمة معينة، لكن تلك القيمة تتغير أكثر مما تريد—وقد لا تعكس أي تغيير فعلي من منظور المستخدم. على سبيل المثال، لنفترض أنك تنشئ كائنoptionsفي جسم مكونك، ثم تقرأ ذلك الكائن من داخل تأثيرك:

يتم تعريف هذا الكائن في جسم المكون، لذا فهوقيمة تفاعلية.عندما تقرأ قيمة تفاعلية كهذه داخل تأثير، فإنك تعلنها كتبعية. وهذا يضمن أن تأثيرك "يتفاعل" مع تغييراتها:

من المهم الإعلان عنها كتبعية! هذا يضمن، على سبيل المثال، أنه إذا تغيرroomId، فإن تأثيرك سيعيد الاتصال بالدردشة باستخدامoptionsالجديدة. ومع ذلك، هناك أيضًا مشكلة في الكود أعلاه. لرؤيتها، حاول الكتابة في حقل الإدخال في الحقل الرملي أدناه، وشاهد ما يحدث في وحدة التحكم:

في الحقل الرملي أعلاه، يقوم حقل الإدخال بتحديث متغير الحالةmessageفقط. من منظور المستخدم، لا ينبغي أن يؤثر هذا على اتصال الدردشة. ومع ذلك، في كل مرة تقوم فيها بتحديثmessage، يعيد مكونك التصيير. عندما يعيد مكونك التصيير، يتم تشغيل الكود بداخله مرة أخرى من البداية.

يتم إنشاء كائنoptionsجديد من البداية في كل إعادة تصيير لمكونChatRoom. يرى React أن كائنoptions هو كائن مختلفعن كائنoptionsالذي تم إنشاؤه أثناء التصيير السابق. وهذا هو السبب في أنه يعيد مزامنة تأثيرك (الذي يعتمد علىoptions)، وتعيد الدردشة الاتصال أثناء الكتابة.

تؤثر هذه المشكلة فقط على الكائنات والدوال. في JavaScript، يعتبر كل كائن ودالة تم إنشاؤه حديثًا مميزًا عن جميع الآخرين. لا يهم أن المحتويات بداخلها قد تكون متطابقة!

يمكن أن تجعل تبعيات الكائنات والدوال تأثيرك يعيد المزامنة أكثر مما تحتاج.

لهذا السبب، يجب أن تحاول قدر الإمكان تجنب استخدام الكائنات والدوال كتبعيات لتأثيرك. بدلاً من ذلك، حاول نقلها خارج المكون، أو داخل التأثير، أو استخراج القيم الأولية منها.

انقل الكائنات والدوال الثابتة خارج مكونك

إذا كان الكائن لا يعتمد على أي خاصيات أو حالة، يمكنك نقل هذا الكائن خارج مكونك:

بهذه الطريقة، أنتتثبتللمدقق أنه ليس تفاعليًا. لا يمكن أن يتغير نتيجة لإعادة التصيير، لذا لا يحتاج إلى أن يكون تبعية. الآن إعادة تصييرChatRoomلن تتسبب في إعادة مزامنة تأثيرك.

هذا ينطبق على الدوال أيضًا:

بما أنcreateOptionsمُعلنة خارج مكونك، فهي ليست قيمة تفاعلية. لهذا السبب لا تحتاج إلى أن تُحدد في تبعيات تأثيرك، ولهذا السبب لن تتسبب أبدًا في إعادة مزامنة تأثيرك.

انقل الكائنات والدوال الديناميكية داخل تأثيرك

إذا كان كائنك يعتمد على بعض القيم التفاعلية التي قد تتغير نتيجة لإعادة التصيير، مثل خاصيةroomId، لا يمكنك سحبهخارجمكونك. ومع ذلك، يمكنك نقل إنشائهداخلكود تأثيرك:

الآن بما أنoptionsمُعلنة داخل تأثيرك، فهي لم تعد تبعية لتأثيرك. بدلاً من ذلك، القيمة التفاعلية الوحيدة التي يستخدمها تأثيرك هيroomId. بما أنroomIdليس كائنًا أو دالة، يمكنك التأكد من أنه لن يكونمختلفًا دون قصد. في جافا سكريبت، تتم مقارنة الأرقام والنصوص حسب محتواها:

بفضل هذا الإصلاح، لم يعد الدردشة تعيد الاتصال إذا قمت بتحرير الإدخال:

ومع ذلك، فإنهيعيدالاتصال عند تغيير القائمة المنسدلةroomId، كما هو متوقع.

هذا ينطبق على الدوال أيضًا:

يمكنك كتابة دوالك الخاصة لتجميع أجزاء من المنطق داخل Effect الخاص بك. طالما أنك تعلن عنها أيضًاداخلEffect الخاص بك، فهي ليست قيم تفاعلية، وبالتالي لا تحتاج إلى أن تكون تبعيات لـ Effect الخاص بك.

قراءة القيم الأولية من الكائنات

في بعض الأحيان، قد تتلقى كائنًا من الخصائص (props):

الخطر هنا هو أن المكون الأب سيُنشئ الكائن أثناء التصيير:

هذا سيؤدي إلى إعادة اتصال Effect الخاص بك في كل مرة يعيد فيها المكون الأب التصيير. لإصلاح هذا، اقرأ المعلومات من الكائنخارجEffect، وتجنب وجود تبعيات من الكائنات والدوال:

يصبح المنطق متكررًا قليلاً (تقرأ بعض القيم من كائن خارج Effect، ثم تنشئ كائنًا بنفس القيم داخل Effect). لكنه يجعل الأمر واضحًا جدًا ما هي المعلومات التي يعتمد عليها Effect الخاص بكفعليًا. إذا أعيد إنشاء الكائن عن غير قصد من قبل المكون الأب، فلن يعيد الدردشة الاتصال. ومع ذلك، إذا كانoptions.roomIdأوoptions.serverUrlمختلفين حقًا، فستعيد الدردشة الاتصال.

حساب القيم الأولية من الدوال

يمكن أن يعمل نفس النهج مع الدوال. على سبيل المثال، افترض أن المكون الأب يمرر دالة:

لتجنب جعلها تبعية (وتسبب إعادة الاتصال عند إعادة التصيير)، استدعها خارج Effect. هذا يمنحك قيمroomId و serverUrlالتي ليست كائنات، ويمكنك قراءتها من داخل Effect الخاص بك:

هذا يعمل فقط مع الدوالالنقيةلأنها آمنة للاستدعاء أثناء التصيير. إذا كانت دالتك هي معالج حدث، لكنك لا تريد أن تتسبب تغييراتها في إعادة مزامنة Effect الخاص بك،فلفها في حدث Effect بدلاً من ذلك.

ملخص

  • يجب أن تتطابق التبعيات دائمًا مع الكود.
  • عندما لا تكون راضيًا عن تبعياتك، ما تحتاج إلى تعديله هو الكود.
  • كتم صوت أداة التحقق من الجودة يؤدي إلى أخطاء مربكة للغاية، ويجب عليك تجنب ذلك دائمًا.
  • لإزالة تبعية، تحتاج إلى "إثبات" لأداة التحقق من الجودة أنها ليست ضرورية.
  • إذا كان يجب تشغيل بعض الكود استجابةً لتفاعل محدد، انقل هذا الكود إلى معالج الأحداث.
  • إذا كان يجب إعادة تشغيل أجزاء مختلفة من التأثير الخاص بك لأسباب مختلفة، قم بتقسيمه إلى عدة تأثيرات.
  • إذا كنت تريد تحديث بعض الحالة بناءً على الحالة السابقة، قم بتمرير دالة تحديث.
  • إذا كنت تريد قراءة أحدث قيمة دون "تفاعلها"، استخرج حدث تأثير من تأثيرك.
  • في جافا سكريبت، تعتبر الكائنات والدوال مختلفة إذا تم إنشاؤها في أوقات مختلفة.
  • حاول تجنب تبعيات الكائنات والدوال. انقلها خارج المكون أو داخل التأثير.

Try out some challenges

Challenge 1 of 4:Fix a resetting interval #

This Effect sets up an interval that ticks every second. You’ve noticed something strange happening: it seems like the interval gets destroyed and re-created every time it ticks. Fix the code so that the interval doesn’t get constantly re-created.