كيفية استخدام سياق التفاعل بأمان

السياق هو ميزة قوية للغاية في React ، مع الكثير من التنازلات من حوله. قليلا مثل الفاكهة المحرمة في الجنة.

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

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

الآن ، من المحتمل أنك اتبعت هذه النصيحة الحكيمة ، ولكن في الوقت نفسه ، فإن استخدام المكتبات التي تستخدم السياق ، على سبيل المثال جهاز التوجيه التفاعلي ، قد لا يزال يواجهك مشكلة عند دمجها مع مكتبات أخرى مثل رد فعل رد الفعل أو رد فعل mobx ، أو حتى عندما مع الجمع بين shouldComponentUpdate الخاصة بك أو تلك المقدمة من React.PureComponent. يمكن العثور على المشكلات القديمة في متتبع مشكلة React وفي متتبعي المشكلات في المكتبات المرتبطة بالتفاعل.

فلماذا هذه المدونة مناسبة لك؟ حسنا إما بسبب

  1. أنت مؤلف مكتبة
  2. تستخدم مكتبة تستخدم سياقًا أو تستخدم سياقًا بنفسك ، وتريد أن تستخدم بأمان shouldComponentUpdate (SCU) أو تطبيقاته (مثل PureComponent أو Redux connect أو MobX observer).

لماذا يكون Context + ShouldComponentUpdate مشكلة؟

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

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

دعنا نوضح مشكلة التداخل هذه في تطبيق بسيط:

يكون التنسيق الإشكالي بين السياق و SCU مرئيًا بوضوح بمجرد الضغط على الزر "Red please!" (في علامة التبويب "Result" أعلاه). يحصل الزر نفسه على لون جديد ، ولكن لا يتم تحديث عناصر المهام. السبب في ذلك بسيط: مكون TodoList لدينا ذكي ؛ إنها تعرف أنه كلما لم تتلق أي عناصر جديدة ، فلن تحتاج إلى إعادة عرضها. (يتم تحقيق الذكاء من خلال وراثة من PureComponent الذي ينفذ ينبغي أن يتم تحديثه).

ومع ذلك ، نظرًا لهذا الذكاء (وهو أمر ضروري للحفاظ على أداء رد الفعل في التطبيقات الكبيرة) ، لا تتلقى مكونات ThemedText داخل قائمة TodoList السياق الجديد بلون محدث! لأن SCU تُرجع خطأ ، لا يتم تحديث قائمة TodoList ولا أي من أحفادها.

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

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

ينبغي أن يعملكنتومبوندات وتحديث السياق معا!

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

  1. يجب ألا يتغير السياق ؛ يجب أن يكون (ضحل) غير قابل للتغيير
  2. يجب أن تتلقى المكونات السياق مرة واحدة فقط ؛ عندما يتم بناؤها.
أو بعبارة أخرى ، يجب ألا نخزن الحالة مباشرة في سياقنا. بدلاً من ذلك ، يجب أن نستخدم السياق كنظام حقن التبعية.

هذا يعني أن SCU لن تتداخل بعد الآن مع السياق الذي يجب تمريره ، لأنه لا يحتاج أبدًا إلى تمرير سياق جديد إلى أطفاله. رائع! هذا يحل جميع مشاكلنا!

التواصل التغييرات من خلال حقن التبعية القائمة على السياق

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

أو ، قائمة runnable كاملة:

لاحظ أن هذا المثال يتفاعل الآن بشكل صحيح مع تغير اللون. ومع ذلك ، لا يزال يستخدم PureComponent. أيضًا ، لم تتغير واجهة برمجة التطبيقات للمكونات المهمة App و TodoList و ThemedText.

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

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

استنتاج

من خلال تقييد استخدام السياق ليكون مجرد نظام حقن التبعية بدلاً من حاوية الحالة ، يمكننا أن نجعل كل من المكتبات المستندة إلى السياق و shouldComponentUpdate يتصرفان بشكل صحيح دون تدخل ودون كسر واجهات برمجة التطبيقات للمستهلكين. والأهم من ذلك أنها تعمل ضمن الحدود الحالية لنظام سياق React. مجرد التمسك بهذه القاعدة البسيطة:

يجب استخدام السياق كما لو كان يتم استلامه مرة واحدة فقط بواسطة كل مكون.

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

التحديث 29-9-2016: نشر Ryan Florence للتو حزمة عامة تستفيد من هذا النمط بحيث لا تضطر إلى كتابة جميع العناصر النحاسية بنفسك: تفاعل-انبعاث السياق

المكافأة: استخدام MobX ملاحظات كما سياق يبسط الأشياء

(هذا القسم مثير للاهتمام بشكل رئيسي إذا كنت تستخدم أو ترغب في MobX)

إذا كنت تستخدم MobX ، فيمكنك فعلًا تخطي عناصر باعث الأحداث بأكملها ، وبدلاً من ذلك ، يمكنك تخزين عناصر الملاحظة (المعبأة في مربع) في السياق والاشتراك فيها باستخدام مكون الديكور / المراقب ذي الترتيب العالي. هذا يلغي الحاجة لإدارة اشتراكات البيانات بنفسك:

في الواقع ، يمكن للمرء أن يجعل الأمر أكثر بساطة عن طريق استخدام آلية الموفر / الحقن التي هي عبارة عن تجريد صغير على آلية سياق React ، المدمجة في MobX. يزيل الملف المصفي لإعلان سياق الأنواع وما شابهها. لاحظ أنه يمكن العثور على مفاهيم مماثلة في libs معممة مثل إعادة التركيب أو نفق التفاعل.

لما يستحق؛ لاحظ أنه على الرغم من أن حلنا الأولي القائم على تقنية DI كان أطول بمقدار 1.5 مرة من قاعدة الشفرة الأصلية ، إلا أن هذا الحل النهائي هو ما دام التنفيذ الأصلي المشكوك فيه.