مشاكل الأنماط الأساسية لإنشاء تطبيقات تعتمد على البيانات على React.JS

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


  1. تحتاج إلى هيكل العناصر التابعة دون داع
  2. أو قم بتمريرها كدعائم ، مما يؤدي إلى تعقيد قابلية القراءة ودلالاتها وهيكلها

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


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

إذا اتبعنا الرابط ، فسنرى حجة أخرى:


في Facebook ، نستخدم React في الآلاف من المكونات ، ولم نعثر على أي حالات نوصي فيها بإنشاء تسلسل هرمي للمكونات.
رد فعل الوثائق التكوين مقابل الميراث.

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


نمط # 1 - المكونات التي تسيطر عليها مباشرة



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


const MovieCard = (props) => { const {title, genre, description, rating, image} = props.data; return ( <div> <img href={image} /> <div><h2>{title}</h2></div> <div><p>{genre}</p></div> <div><p>{description}</p></div> <div><h1>{rating}</h1></div> </div> ) } 

إيقاف. نحن نعرف بالفعل عن التوسع غير المحدود لمتطلبات المكون. فجأة سوف يكون العنوان رابط لمراجعة الفيلم؟ وهذا النوع - لأفضل الأفلام منه؟ لا تضيف الآن:


 const MovieCard = (props) => { const {title: {title}, description: {description}, rating: {rating}, genre: {genre}, image: {imageHref} } = props.data; return ( <div> <img href={imageHref} /> <div><h2>{name}</h2></div> <div><p>{genre}</p></div> <div><h1>{rating}</h1></div> <div><p>{description}</p></div> </div> ) } 

لذلك سوف نحمي أنفسنا من المشاكل في المستقبل ، ولكن نفتح الباب لخطأ نقطة الصفر. في البداية ، يمكننا رمي الهياكل مباشرة من الخلف:


 <MovieCard data={res.data} /> 

الآن في كل مرة تحتاج إلى تكرار جميع المعلومات:


 <MovieCard data={{ title: {res.title}, description: {res.description}, rating: {res.rating}, image: {res.imageHref} }} /> 

ومع ذلك ، نسينا هذا النوع - وسقط المكون. وإذا لم يتم تعيين محددات الأخطاء ، فسيكون التطبيق بالكامل معها.


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


 interface IMovieCardElement { text?: string; } interface IMovieCardImage { imageHref?: string; } interface IMovieCardProps { title: IMovieCardElement; description: IMovieCardElement; rating: IMovieCardElement; genre: IMovieCardElement; image: IMovieCardImage; } ... const {title: {text: title}, description: {text: description}, rating: {text: rating}, genre: {text: genre}, image: {imageHref} } = props.data; 

لتوفير الوقت ، سنستمر في نقل البيانات "كما هي" أو "كـ IMovieCardProps". ما تبين؟ لقد سبق أن وصفنا ثلاث مرات (إذا استخدمت في مكان واحد ) بنية بيانات واحدة . وماذا لدينا؟ مكون لا يزال لا يمكن تعديله. مكون يمكن أن يحطم التطبيق بأكمله.


حان الوقت لإعادة استخدام هذا المكون. لم تعد هناك حاجة التقييم. لدينا خياران:


ضع دعامة بدون تصنيف أينما كان التصنيف مطلوبًا


 const MovieCard = ({withoutRating, ...props}) => { const {title: {title}, description: {description}, rating: {rating}, genre: {genre}, image: {imageHref} } = props.data; return ( <div> <img href={imageHref} /> <div><h2>{name}</h2></div> <div><p>{genre}</p></div> { withoutRating && <div><h1>{rating}</h1></div> } <div><p>{description}</p></div> </div> ) } 

سريع ، لكننا نجمع الدعائم ونبني بنية بيانات رابعة.


جعل التصنيف في IMovieCardProps اختياريًا. لا تنسَ أن تجعله كائنًا فارغًا افتراضيًا


 const MovieCard = ({data, ...props}) => { const {title: {text: title}, description: {text: description}, rating: {text: rating} = {}, genre: {text: genre}, image: {imageHref} } = data; return ( <div> <img href={imageHref} /> <div><h2>{name}</h2></div> <div><p>{genre}</p></div> { data.rating && <div><h1>{rating}</h1></div> } <div><p>{description}</p></div> </div> ) } 

أصعب ، ولكن يصبح من الصعب قراءة التعليمات البرمجية. مرة أخرى ، كرر للمرة الرابعة . التحكم في المكون غير واضح ، حيث يتم التحكم به بشكل غير شفاف بواسطة بنية البيانات. لنفترض أنه طُلب منا أن نجعل التقييم سيئ السمعة رابطًا ، ولكن ليس في كل مكان:


  rating: {text: rating, url: ratingUrl} = {}, ... { data.rating && data.rating.url ? <div>><h1><a href={ratingUrl}{rating}</a></h1></div> : <div><h1>{rating}</h1></div> } 

وهنا نواجه المنطق المعقد الذي يملي بنية البيانات غير الشفافة.


نمط رقم 2 - مكونات مع حالتها الخاصة ومخفضات



في الوقت نفسه ، نهج غريب وشعبية. لقد استخدمتها عندما بدأت العمل مع React وكانت وظيفة JSX في Vue مفقودة. لقد سمعت أكثر من مرة من المطورين في mitaps أن هذا النهج يسمح لك بتخطي المزيد من هياكل البيانات المعممة:


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

وبطبيعة الحال ، تستكمل مشكلة العتامة (1) بمشكلة التحميل الزائد للمنطق (2) وإضافة الحالة إلى المكون النهائي (3).


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


يتم طرح البيانات باسم البرنامج النصي (عادة ما تكون سلسلة):


 <MovieCard data={{ type: 'withoutRating', data: res.data, }} /> 

اكتب الآن المكون:


 const MovieCard = ({data}) => { const card = reduceData(data.type, data.data); return ( <div> <img href={card.imageHref} /> <div><h2>{card.name}</h2></div> <div><p>{card.genre}</p></div> { card.withoutRating && <div><h1>{card.rating}</h1></div> } <div><p>{card.description}</p></div> </div> ) } 

والمنطق:


 const reduceData = (type, data) = { switch (type) { case 'withoutRating': return { title: {data.title}, description: {data.description}, rating: {data.rating}, genre: {data.genre}, image: {data.imageHref} withoutRating: true, }; ... } }; 

هناك العديد من المشكلات في هذه الخطوة:


  1. بإضافة طبقة من المنطق ، نفقد أخيرًا الاتصال المباشر بين البيانات والشاشة
  2. إن ازدواجية المنطق لكل حالة يعني أنه في حالة احتياج جميع البطاقات إلى تصنيف عمر ، يجب تسجيلها في كل مخفض
  3. المشاكل الأخرى المتبقية من الخطوة رقم 1

نمط # 3 - نقل منطق العرض والبيانات لإدارة الدولة



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


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


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


الحل: نمط رقم 4 - التكوين



طريقة التركيب واضحة إذا اتبعت المبادئ التالية (بصرف النظر عن نهج مماثل في أنماط التصميم ):


  1. يستخدم تطوير الواجهة الأمامية - تطوير واجهات المستخدم - HTML للتخطيط
  2. يستخدم Javascript لاستلام البيانات ونقلها ومعالجتها.

لذلك ، قم بنقل البيانات من مجال إلى آخر في أقرب وقت ممكن. يستخدم React تجريد JSX لقالب HTML ، ولكنه في الحقيقة يستخدم مجموعة من أساليب createElement. بمعنى ، يجب التعامل مع مكونات JSX و React ، وهي أيضًا عناصر JSX ، كوسيلة للعرض والسلوك ، بدلاً من تحويل ومعالجة البيانات التي يجب أن تحدث على مستوى منفصل.


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


 function SplitPane(props) { return ( <div className="SplitPane"> <div className="SplitPane-left"> {props.left} </div> <div className="SplitPane-right"> {props.right} </div> </div> ); } function App() { return ( <SplitPane left={ <Contacts /> } right={ <Chat /> } /> ); } 

وهذا هو ، كمعلمات ، بدلاً من السلاسل والأرقام وأنواع المنطقية ، يتم نقل المكونات الجاهزة.


للأسف ، اتضح أيضًا أن هذه الطريقة غير مرنة. إليك السبب:


  1. كلا الدعائم مطلوبة. هذا يحد من إعادة استخدام المكون
  2. اختياري يعني التحميل الزائد المكون SplitPane مع المنطق.
  3. لا يتم عرض التداخل والتعدد بشكل كبير جدًا.
  4. يجب إعادة كتابة منطق التعيين هذا لكل مكون يقبل الدعائم.

نتيجةً لذلك ، يمكن أن ينمو هذا الحل في التعقيد حتى بالنسبة للسيناريوهات البسيطة إلى حد ما:


 function SplitPane(props) { return ( <div className="SplitPane"> { props.left && <div className="SplitPane-left"> {props.left} </div> } { props.right && <div className="SplitPane-right"> {props.right} </div> } </div> ); } function App() { return ( <SplitPane left={ contacts.map(el => <Contacts name={ <ContactsName name={el.name} /> } phone={ <ContactsPhone name={el.phone} /> } /> ) } right={ <Chat /> } /> ); } 

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


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


باختصار ، شيء من هذا القبيل ، دعنا نسميها النموذج رقم 5 - فتحات :


 function App() { return ( <SplitPane> <LeftPane> <Contacts /> </LeftPane> <RightPane> <Chat /> </RightPane> </SplitPane> ); } 

سأتحدث عن هذا في المقالة التالية حول الحلول الحالية لنمط الفتحة في React وعن الحل الخاص بي.

Source: https://habr.com/ru/post/ar475170/


All Articles