دورة حياة التأثيرات التفاعلية
للتأثيرات دورة حياة مختلفة عن المكونات. يمكن للمكونات أن تُحمل، أو تُحدث، أو تُفك تحميلها. يمكن للتأثير أن يقوم بشيئين فقط: بدء مزامنة شيء ما، ثم لاحقًا إيقاف مزامنته. يمكن أن تحدث هذه الدورة عدة مرات إذا كان تأثيرك يعتمد على الخصائص والحالة التي تتغير بمرور الوقت. يوفر React قاعدة مدقق للتحقق من أنك حددت تبعيات تأثيرك بشكل صحيح. هذا يحافظ على مزامنة تأثيرك مع أحدث الخصائص والحالة.
سوف تتعلم
- كيف تختلف دورة حياة التأثير عن دورة حياة المكون
- كيف تفكر في كل تأثير على حدة بمعزل عن غيره
- متى يحتاج تأثيرك إلى إعادة المزامنة، ولماذا
- كيف يتم تحديد تبعيات تأثيرك
- ماذا يعني أن تكون القيمة تفاعلية
- ماذا تعني مصفوفة التبعيات الفارغة
- كيف يتحقق React من صحة تبعياتك باستخدام المدقق
- ماذا تفعل عندما لا تتفق مع المدقق
دورة حياة التأثير
يمر كل مكون React بنفس دورة الحياة:
- يتم تحميلالمكون عند إضافته إلى الشاشة.
- يتم تحديثالمكون عند استلامه خصائص أو حالة جديدة، عادةً استجابةً لتفاعل.
- يتم فك تحميلالمكون عند إزالته من الشاشة.
إنها طريقة جيدة للتفكير في المكونات، لكنهاليستطريقة للتفكير في التأثيرات.بدلاً من ذلك، حاول التفكير في كل تأثير بشكل مستقل عن دورة حياة مكونك. يصف التأثير كيفيةمزامنة نظام خارجيمع الخصائص والحالة الحالية. مع تغير الكود الخاص بك، ستكون هناك حاجة لحدوث المزامنة بشكل أكثر أو أقل تكرارًا.
لتوضيح هذه النقطة، ضع في اعتبارك هذا التأثير الذي يربط مكونك بخادم دردشة:
يحدد جسم تأثيرك كيفيةبدء المزامنة:
تحدد دالة التنظيف التي يعيدها تأثيرك كيفيةإيقاف المزامنة:
بشكل بديهي، قد تعتقد أن React سوفيبدأ المزامنةعندما يتم تحميل مكونك ويوقف المزامنةعندما يتم فك تحميل مكونك. ومع ذلك، هذه ليست نهاية القصة! في بعض الأحيان، قد يكون من الضروري أيضًابدء وإيقاف المزامنة عدة مراتبينما يظل المكون محملاً.
دعنا نلقي نظرة علىلماذاهذا ضروري،متىيحدث، وكيفيمكنك التحكم في هذا السلوك.
ملاحظة
بعض التأثيرات لا تعيد دالة تنظيف على الإطلاق.في كثير من الأحيان،سترغب في إرجاع واحدة — ولكن إذا لم تفعل، سيتصرف React كما لو أنك أعدت دالة تنظيف فارغة.
لماذا قد تحتاج المزامنة إلى الحدوث أكثر من مرة
تخيل أن مكونChatRoomهذا يستقبل خاصيةroomIdيختارها المستخدم في قائمة منسدلة. لنفترض أن المستخدم يختار في البداية غرفة"general" كـ roomId. يعرض تطبيقك غرفة الدردشة"general":
بعد عرض واجهة المستخدم، سيشغل React تأثيرك لـبدء المزامنة.يتصل بغرفة"general":
حتى الآن، الأمور تسير على ما يرام.
لاحقًا، يختار المستخدم غرفة مختلفة في القائمة المنسدلة (على سبيل المثال،"travel"). أولاً، سيقوم React بتحديث واجهة المستخدم:
فكر فيما يجب أن يحدث بعد ذلك. يرى المستخدم أن"travel"هي غرفة الدردشة المحددة في واجهة المستخدم. ومع ذلك، فإن Effect الذي تم تنفيذه في المرة السابقة لا يزال متصلاً بغرفة"general". لقد تغيرت الخاصيةroomId، لذا فإن ما فعله Effect الخاص بك آنذاك (الاتصال بغرفة"general") لم يعد يتطابق مع واجهة المستخدم.
في هذه المرحلة، تريد من React أن يقوم بأمرين:
- إيقاف المزامنة مع
roomIdالقديمة (قطع الاتصال بغرفة"general") - بدء المزامنة مع
roomIdالجديدة (الاتصال بغرفة"travel")
لحسن الحظ، لقد علمت React بالفعل كيفية القيام بكلا الأمرين!يحدد جسم Effect الخاص بك كيفية بدء المزامنة، وتحدد دالة التنظيف الخاصة بك كيفية إيقاف المزامنة. كل ما يحتاجه React الآن هو استدعاؤهما بالترتيب الصحيح وبالخاصيات والحالة الصحيحة. دعنا نرى كيف يحدث ذلك بالضبط.
كيف يعيد React مزامنة Effect الخاص بك
تذكر أن مكونChatRoomالخاص بك قد تلقى قيمة جديدة لخاصيتهroomId. كانت القيمة السابقة هي"general"، والآن أصبحت"travel". يحتاج React إلى إعادة مزامنة Effect الخاص بك لإعادة توصيلك بغرفة مختلفة.
لـإيقاف المزامنة،سيقوم React باستدعاء دالة التنظيف التي أرجعهما Effect الخاص بك بعد الاتصال بغرفة"general". نظرًا لأنroomIdكانت"general"، فإن دالة التنظيف تقطع الاتصال بغرفة"general":
ثم سيقوم React بتشغيل Effect الذي قدمته خلال عملية التصيير هذه. هذه المرة،roomIdهي"travel"لذا سيقوم بـبدء المزامنةمع غرفة الدردشة"travel"(حتى يتم استدعاء دالة التنظيف الخاصة بها أيضًا في النهاية):
بفضل هذا، أنت الآن متصل بنفس الغرفة التي اختارها المستخدم في واجهة المستخدم. تم تجنب الكارثة!
في كل مرة بعد إعادة تصيير مكونك بقيمة مختلفة لـroomId، سيعيد Effect الخاص بك المزامنة. على سبيل المثال، لنفترض أن المستخدم يغيرroomIdمن"travel"إلى"music". سيقوم React مرة أخرى بـإيقاف مزامنةEffect الخاص بك عن طريق استدعاء دالة التنظيف الخاصة به (قطع اتصالك بغرفة"travel"). ثم سيقوم بـبدء المزامنةمرة أخرى عن طريق تشغيل جسمه بقيمة الخاصية الجديدةroomId(توصيلك بغرفة"music").
أخيرًا، عندما ينتقل المستخدم إلى شاشة مختلفة، يتم إلغاء تحميلChatRoom. الآن لم يعد هناك حاجة للبقاء متصلاً على الإطلاق. سيقوم React بـإيقاف مزامنةEffect الخاص بك للمرة الأخيرة وقطع اتصالك بغرفة الدردشة"music".
التفكير من منظور Effect
دعنا نلخص كل ما حدث من منظور مكونChatRoom:
ChatRoomتم تحميله مع تعيينroomIdإلى"general"ChatRoomتم تحديثه مع تعيينroomIdإلى"travel"ChatRoomتم تحديثه مع تعيينroomIdإلى"music"ChatRoomتم إلغاء تحميله
خلال كل من هذه النقاط في دورة حياة المكون، قام التأثير الخاص بك بأشياء مختلفة:
- تأثيرك اتصل بغرفة
"general" - تأثيرك قطع الاتصال من غرفة
"general"واتصل بغرفة"travel" - تأثيرك قطع الاتصال من غرفة
"travel"واتصل بغرفة"music" - تأثيرك قطع الاتصال من غرفة
"music"
الآن دعنا نفكر فيما حدث من منظور التأثير نفسه:
قد يلهمك هيكل هذا الكود لترى ما حدث كسلسلة من الفترات الزمنية غير المتداخلة:
- تأثيرك اتصل بغرفة
"general"(حتى انقطع الاتصال) - تأثيرك اتصل بغرفة
"travel"(حتى انقطع الاتصال) - تأثيرك اتصل بغرفة
"music"(حتى انقطع الاتصال)
سابقًا، كنت تفكر من منظور المكون. عندما نظرت من منظور المكون، كان من المغري التفكير في التأثيرات على أنها "استدعاءات راجعة" أو "أحداث دورة حياة" تُنفَّذ في وقت محدد مثل "بعد التصيير" أو "قبل الفك". يصبح هذا النمط من التفكير معقدًا بسرعة كبيرة، لذا من الأفضل تجنبه.
بدلًا من ذلك، ركز دائمًا على دورة بدء/إيقاف واحدة في كل مرة. لا ينبغي أن يهم ما إذا كان المكون يُحَمَّل أو يُحدَّث أو يُفَك. كل ما عليك فعله هو وصف كيفية بدء المزامنة وكيفية إيقافها. إذا قمت بذلك بشكل جيد، سيكون تأثيرك قويًا ضد البدء والإيقاف عدة مرات كما هو مطلوب.
قد يذكرك هذا بأنك لا تفكر فيما إذا كان المكون يُحَمَّل أو يُحدَّث عندما تكتب منطق التصيير الذي ينشئ JSX. أنت تصف ما يجب أن يكون على الشاشة، ورياكتيتكفل بالباقي.
كيف تتحقق رياكت من قدرة تأثيرك على إعادة المزامنة
إليك مثال حي يمكنك التجربة معه. اضغط على "فتح الدردشة" لتحميل مكونChatRoom:
لاحظ أنه عندما يُحَمَّل المكون لأول مرة، ترى ثلاث سجلات:
✅ Connecting to "general" room at https://localhost:1234...(للتنمية فقط)❌ Disconnected from "general" room at https://localhost:1234.(للتنمية فقط)✅ Connecting to "general" room at https://localhost:1234...
السجلان الأولان مخصصان للتنمية فقط. في بيئة التطوير، يقوم React دائمًا بإعادة تركيب كل مكون مرة واحدة.
يتأكد React من قدرة Effect الخاص بك على إعادة المزامنة عن طريق إجباره على القيام بذلك فورًا في بيئة التطوير.قد يذكرك هذا بفتح باب وإغلاقه مرة إضافية للتحقق من عمل قفل الباب. يبدأ React ويوقف Effect الخاص بك مرة إضافية في بيئة التطوير للتحقق منأنك قمت بتنفيذ وظيفة التنظيف الخاصة به بشكل جيد.
السبب الرئيسي لإعادة مزامنة Effect الخاص بك عمليًا هو إذا تغيرت بعض البيانات التي يستخدمها. في الحقل التجريبي أعلاه، قم بتغيير غرفة الدردشة المحددة. لاحظ كيف، عندما يتغيرroomId، يتم إعادة مزامنة Effect الخاص بك.
ومع ذلك، هناك أيضًا حالات غير عادية أكثر تتطلب إعادة المزامنة. على سبيل المثال، حاول تعديلserverUrlفي الحقل التجريبي أعلاه أثناء فتح الدردشة. لاحظ كيف تتم إعادة مزامنة Effect استجابةً لتعديلاتك على الكود. في المستقبل، قد تضيف React ميزات إضافية تعتمد على إعادة المزامنة.
كيف يعرف React أنه يحتاج إلى إعادة مزامنة Effect
قد تتساءل كيف عرف React أن Effect الخاص بك يحتاج إلى إعادة مزامنة بعد تغييرroomId. السبب هو أنأنت أخبرت Reactبأن الكود الخاص به يعتمد علىroomIdمن خلال تضمينه فيقائمة التبعيات:
إليك كيفية عمل هذا:
- كنت تعلم أن
roomIdهو خاصية، مما يعني أنه يمكن أن يتغير بمرور الوقت. - كنت تعلم أن Effect الخاص بك يقرأ
roomId(لذا فإن منطقه يعتمد على قيمة قد تتغير لاحقًا). - لهذا السبب حددته كتبعية لـ Effect الخاص بك (حتى تتم إعادة مزامنته عندما يتغير
roomId).
في كل مرة بعد إعادة عرض المكون الخاص بك، سينظر React إلى مصفوفة التبعيات التي قمت بتمريرها. إذا كانت أي من القيم في المصفوفة مختلفة عن القيمة في نفس الموضع التي قمت بتمريرها أثناء العرض السابق، فسيقوم React بإعادة مزامنة Effect الخاص بك.
على سبيل المثال، إذا قمت بتمرير["general"]أثناء العرض الأولي، وقمت لاحقًا بتمرير["travel"]أثناء العرض التالي، فسيقوم React بمقارنة"general" و "travel". هذه قيم مختلفة (مقارنة بـObject.is)، لذا سيقوم React بإعادة مزامنة Effect الخاص بك. من ناحية أخرى، إذا أعاد المكون الخاص بك العرض ولكنroomIdلم يتغير، فسيظل Effect الخاص بك متصلًا بنفس الغرفة.
يمثل كل Effect عملية مزامنة منفصلة
قاوم إضافة منطق غير ذي صلة إلى Effect الخاص بك فقط لأن هذا المنطق يحتاج إلى التشغيل في نفس وقت Effect قمت بكتابته بالفعل. على سبيل المثال، لنفترض أنك تريد إرسال حدث تحليلي عندما يزور المستخدم الغرفة. لديك بالفعل Effect يعتمد علىroomId، لذا قد تشعر بالإغراء لإضافة استدعاء التحليلات هناك:
لكن تخيل أنك تضيف لاحقًا تبعية أخرى إلى هذا Effect تحتاج إلى إعادة إنشاء الاتصال. إذا أعاد هذا Effect المزامنة، فسيستدعي أيضًاlogVisit(roomId)لنفس الغرفة، وهو ما لم تكن تنوي. تسجيل الزيارةهي عملية منفصلةعن الاتصال. اكتبهما كـ Effect منفصلين:
يجب أن يمثل كل Effect في الكود الخاص بك عملية مزامنة منفصلة ومستقلة.
في المثال أعلاه، لن يؤدي حذف Effect واحد إلى كسر منطق Effect الآخر. هذه إشارة جيدة على أنهما يزامنان أشياء مختلفة، وبالتالي كان من المنطقي فصلهما. من ناحية أخرى، إذا قمت بتقسيم قطعة منطقية متماسكة إلى Effects منفصلة، فقد يبدو الكود "أنظف" ولكنه سيكونأصعب في الصيانة.لهذا السبب يجب أن تفكر فيما إذا كانت العمليات نفسها أم منفصلة، وليس فيما إذا كان الكود يبدو أنظف.
تتفاعل Effects مع القيم التفاعلية
يقرأ Effect الخاص بك متغيرين (serverUrl و roomId)، لكنك حددت فقطroomIdكتبعية:
لماذا لا تحتاجserverUrlإلى أن تكون تبعية؟
هذا لأنserverUrlلا تتغير أبدًا بسبب إعادة التصيير. إنها دائمًا نفسها بغض النظر عن عدد مرات إعادة تصيير المكون والسبب. بما أنserverUrlلا تتغير أبدًا، فلن يكون من المنطقي تحديدها كتبعية. ففي النهاية، التبعيات تفعل شيئًا فقط عندما تتغير مع مرور الوقت!
من ناحية أخرى، قد يكونroomIdمختلفًا عند إعادة التصيير.الخاصيات والحالة والقيم الأخرى المُعلن عنها داخل المكون هيتفاعليةلأنها تُحسب أثناء التصيير وتشارك في تدفق بيانات React.
إذا كانتserverUrlمتغير حالة، لكانت تفاعلية. يجب تضمين القيم التفاعلية في التبعيات:
بإدراجserverUrlكتبعية، تضمن إعادة مزامنة التأثير بعد تغييرها.
جرب تغيير غرفة الدردشة المحددة أو تحرير عنوان URL للخادم في هذا الحوض الرملي:
كلما قمت بتغيير قيمة تفاعلية مثلroomIdأوserverUrl، يعيد التأثير الاتصال بخادم الدردشة.
ماذا يعني التأثير ذو التبعيات الفارغة
ماذا يحدث إذا نقلت كلًا منserverUrl و roomIdخارج المكون؟
الآن لا يستخدم كود التأثير الخاص بكأيقيم تفاعلية، لذا يمكن أن تكون تبعياته فارغة ([]).
بالنظر من منظور المكون، فإن مصفوفة التبعيات الفارغة[]تعني أن هذا التأثير يتصل بغرفة الدردشة فقط عندما يركب المكون، وينفصل فقط عندما يُفكك المكون. (تذكر أن React ستظلتعيد مزامنته مرة إضافيةفي وضع التطوير لاختبار منطقك.)
ومع ذلك، إذا كنتتفكر من منظور التأثير،فلن تحتاج إلى التفكير في التركيب والفك على الإطلاق. المهم هو أنك حددت ما يفعله تأثيرك لبدء وإيقاف المزامنة. اليوم، ليس لديه تبعيات تفاعلية. ولكن إذا أردت يومًا أن يغير المستخدمroomIdأوserverUrlمع مرور الوقت (وستصبح تفاعلية)، فلن يتغير كود تأثيرك. ستحتاج فقط إلى إضافتها إلى التبعيات.
جميع المتغيرات المُعلن عنها في جسم المكون هي تفاعلية
الخاصيات والحالة ليست القيم التفاعلية الوحيدة. القيم التي تحسبها منها هي أيضًا تفاعلية. إذا تغيرت الخاصيات أو الحالة، فسيعيد مكونك التصيير، وستتغير القيم المحسوبة منها أيضًا. هذا هو السبب في أن جميع المتغيرات من جسم المكون التي يستخدمها التأثير يجب أن تكون في قائمة تبعيات التأثير.
لنفترض أن المستخدم يمكنه اختيار خادم دردشة في القائمة المنسدلة، ولكن يمكنه أيضًا تكوين خادم افتراضي في الإعدادات. افترض أنك وضعت بالفعل حالة الإعدادات فيسياقلذا تقرأsettingsمن ذلك السياق. الآن تحسبserverUrlبناءً على الخادم المحدد من الخاصيات والخادم الافتراضي:
في هذا المثال،serverUrlليس خاصية (prop) ولا متغير حالة. إنه متغير عادي تحسبه أثناء التصيير. لكنه يُحسب أثناء التصيير، لذا يمكن أن يتغير بسبب إعادة التصيير. هذا هو السبب في كونه تفاعليًا.
جميع القيم داخل المكون (بما في ذلك الخصائص والحالة والمتغيرات في جسم المكون) هي قيم تفاعلية. أي قيمة تفاعلية يمكن أن تتغير عند إعادة التصيير، لذا تحتاج إلى تضمين القيم التفاعلية كتبعيات للتأثير (Effect).
بمعنى آخر، التأثيرات "تتفاعل" مع جميع القيم من جسم المكون.
يتأكد React من أنك حددت كل قيمة تفاعلية كتبعية
إذا كانت أداة التحقق (linter) الخاصة بكمضبوطة لـ React،فستتحقق من أن كل قيمة تفاعلية مستخدمة في كود التأثير الخاص بك مُعلن عنها كتبعية له. على سبيل المثال، هذا خطأ في التحقق لأن كلًا منroomId و serverUrlتفاعليان:
قد يبدو هذا خطأ في React، لكن في الحقيقة يشير React إلى خطأ في كودك. كل منroomId و serverUrlقد يتغيران بمرور الوقت، لكنك تنسى إعادة مزامنة تأثيرك عندما يتغيران. ستبقى متصلاً بـroomId و serverUrlالأوليين حتى بعد أن يختار المستخدم قيمًا مختلفة في واجهة المستخدم.
لإصلاح الخطأ، اتبع اقتراح أداة التحقق لتحديدroomId و serverUrlكتبعيات لتأثيرك:
جرب هذا الإصلاح في صندوق التجربة أعلاه. تحقق من اختفاء خطأ أداة التحقق، وأن الدردجة تتصل مرة أخرى عند الحاجة.
ماذا تفعل عندما لا تريد إعادة المزامنة
في المثال السابق، أصلحت خطأ أداة التحقق بإدراجroomId و serverUrlكتبعيات.
ومع ذلك، يمكنك بدلاً من ذلك "إثبات" لأداة التحقق أن هذه القيم ليست قيمًا تفاعلية،أي أنهالا يمكنأن تتغير نتيجة لإعادة التصيير. على سبيل المثال، إذا كانserverUrl و roomIdلا يعتمدان على التصيير ودائمًا لهما نفس القيم، يمكنك نقلهما خارج المكون. الآن لا يحتاجان إلى أن يكونا تبعيات:
يمكنك أيضًا نقلهاداخل الـ Effect.فهي لا تُحسب أثناء التصيير، وبالتالي فهي ليست تفاعلية:
الـ Effects هي كتل كود تفاعلية.تعيد المزامنة عندما تتغير القيم التي تقرأها داخلها. على عكس معالجات الأحداث التي تعمل مرة واحدة لكل تفاعل، تعمل الـ Effects كلما كانت المزامنة ضرورية.
لا يمكنك "اختيار" تبعياتك.يجب أن تتضمن تبعياتك كلقيمة تفاعليةتقرأها في الـ Effect. يفرض أداة التحقق (linter) ذلك. قد يؤدي هذا أحيانًا إلى مشاكل مثل الحلقات اللانهائية وإعادة مزامنة الـ Effect كثيرًا. لا تحل هذه المشاكل بقمع أداة التحقق! إليك ما يمكن تجربته بدلاً من ذلك:
- تأكد من أن Effect الخاص بك يمثل عملية مزامنة مستقلة.إذا كان Effect الخاص بك لا يزامن أي شيء،فقد يكون غير ضروري.إذا كان يزامن عدة أشياء مستقلة،فقسّمه.
- إذا كنت تريد قراءة أحدث قيمة للـ props أو الـ state دون "الاستجابة" لها وإعادة مزامنة الـ Effect،يمكنك تقسيم Effect الخاص بك إلى جزء تفاعلي (ستبقيه في الـ Effect) وجزء غير تفاعلي (ستستخرجه إلى شيء يسمىEffect Event).اقرأ عن فصل الأحداث عن الـ Effects.
- تجنب الاعتماد على الكائنات والدوال كتبعيات.إذا قمت بإنشاء كائنات ودوال أثناء التصيير ثم قرأتها من Effect، ستكون مختلفة في كل عملية تصيير. سيؤدي هذا إلى إعادة مزامنة Effect الخاص بك في كل مرة.اقرأ المزيد عن إزالة التبعيات غير الضرورية من الـ Effects.
مشكلة محتملة
أداة التحقق (linter) هي صديقك، لكن قدراتها محدودة. تعرف أداة التحقق فقط عندما تكون التبعياتخاطئة. إنها لا تعرفأفضلطريقة لحل كل حالة. إذا اقترحت أداة التحقق تبعية، لكن إضافتها تسبب حلقة، فهذا لا يعني أنه يجب تجاهل أداة التحقق. تحتاج إلى تغيير الكود داخل (أو خارج) الـ Effect بحيث لا تكون تلك القيمة تفاعلية ولاتحتاجإلى أن تكون تبعية.
إذا كان لديك قاعدة كود موجودة، قد يكون لديك بعض الـ Effects التي تقمع أداة التحقق هكذا:
في الصفحةالتالية، ستتعلم كيفية إصلاح هذا الكود دون كسر القواعد. يستحق الإصلاح دائمًا!
ملخص
- يمكن للمكونات أن تُحمّل، وتُحدّث، وتُفكّك.
- لكل Effect دورة حياة منفصلة عن المكون المحيط به.
- يصف كل Effect عملية مزامنة منفصلة يمكنها أنتبدأ و تتوقف.
- عند كتابة وقراءة الـ Effects، فكر من منظور كل Effect على حدة (كيفية بدء وإيقاف المزامنة) بدلاً من منظور المكون (كيف يُحمّل، يُحدّث، أو يُفكّك).
- القيم المُعلن عنها داخل جسم المكون هي "تفاعلية".
- يجب أن تعيد القيم التفاعلية مزامنة الـ Effect لأنها يمكن أن تتغير مع الوقت.
- تتحقق أداة التحقق (linter) من أن جميع القيم التفاعلية المستخدمة داخل الـ Effect محددة كتبعيات.
- جميع الأخطاء التي تكتشفها أداة التحقق شرعية. هناك دائمًا طريقة لإصلاح الكود دون كسر القواعد.
Try out some challenges
Challenge 1 of 5:Fix reconnecting on every keystroke #
In this example, the ChatRoom component connects to the chat room when the component mounts, disconnects when it unmounts, and reconnects when you select a different chat room. This behavior is correct, so you need to keep it working.
However, there is a problem. Whenever you type into the message box input at the bottom, ChatRoom also reconnects to the chat. (You can notice this by clearing the console and typing into the input.) Fix the issue so that this doesn’t happen.
