مكونات صغيرة: ماذا يمكن أن يحدث الخطأ؟ نحن نستخدم مبدأ المسؤولية الوحيدة

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


صور أوستن كيرك مع Unsplash

تتمثل ميزة نظام React Component (والمكتبات المشابهة) في تقسيم واجهة المستخدم الخاصة بك إلى أجزاء صغيرة يسهل قراءتها وإعادة استخدامها.

هذه المكونات مدمجة (100-200 سطر) ، والتي تتيح للمطورين الآخرين فهمها وتعديلها بسهولة.

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

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

في هذه المقالة ، سأثبت أن معظم المكونات (حتى مع طول 200 سطر المعتاد) يجب أن تكون أكثر استهدافًا بدقة. يجب أن تؤدي وظيفة واحدة فقط ، وأداء ذلك جيدا. هذا ما يقوله إدي عثماني عظيم هنا .

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

دعونا نوضح كيف ، عند إنشاء المكونات ، قد يحدث خطأ ما .

لدينا التطبيق


تخيل أن لدينا تطبيقًا قياسيًا للمدونين. وهنا ما على الشاشة الرئيسية:

class Main extends React.Component { render() { return ( <div> <header> // Header JSX </header> <aside id="header"> // Sidebar JSX </aside> <div id="post-container"> {this.state.posts.map(post => { return ( <div className="post"> // Post JSX </div> ); })} </div> </div> ); } } 

(يجب اعتبار هذا المثال ، مثل العديد من الأمثلة اللاحقة ، رمزًا زائفًا.)

يعرض اللوحة العلوية والشريط الجانبي وقائمة المنشورات. كل شيء بسيط.

نظرًا لأننا نحتاج أيضًا إلى تنزيل المنشورات ، يمكننا القيام بذلك أثناء تثبيت المكون:

 class Main extends React.Component { state = { posts: [] }; componentDidMount() { this.loadPosts(); } loadPosts() { // Load posts and save to state } render() { // Render code } } 

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

 class Main extends React.Component { state = { posts: [], isSidebarOpen: false }; componentDidMount() { this.loadPosts(); } loadPosts() { // Load posts and save to state } handleOpenSidebar() { // Open sidebar by changing state } handleCloseSidebar() { // Close sidebar by changing state } render() { // Render code } } 

أصبح مكوننا أكثر تعقيدًا بعض الشيء ، ولكن لا يزال من السهل قراءته.

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

ينص مبدأ المسؤولية الفردية على أن المكون الواحد يجب أن يؤدي وظيفة واحدة فقط. إذا أعدنا صياغة التعريف المأخوذ من wikipedia.org ، اتضح أن كل مكون يجب أن يكون مسؤولاً عن جزء واحد فقط من الوظيفة [التطبيق].

المكون الرئيسي لدينا يلبي هذا المطلب. ما هي المشكلة؟

فيما يلي صيغة مختلفة للمبدأ: يجب أن يكون لأي [مكون] سبب واحد فقط للتغيير .

هذا التعريف مأخوذ من كتاب روبرت مارتن ، Rapid Software Development. المبادئ ، الأمثلة ، الممارسة ، وهي ذات أهمية كبيرة.

من خلال التركيز على سبب واحد لتغيير مكوناتنا ، يمكننا إنشاء تطبيقات أفضل يسهل تكوينها.

من أجل الوضوح ، دعنا نعقد مكوننا.

تعقيد


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

هذا ليس من الصعب القيام به!

 class Main extends React.Component { state = { posts: [], isSidebarOpen: false, postsToHide: [] }; // older methods get filteredPosts() { // Return posts in state, without the postsToHide } render() { return ( <div> <header> // Header JSX </header> <aside id="header"> // Sidebar JSX </aside> <div id="post-container"> {this.filteredPosts.map(post => { return ( <div className="post"> // Post JSX </div> ); })} </div> </div> ); } } 

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

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

 class Main extends React.Component { state = { posts: [], isSidebarOpen: false, postsToHide: [], isMobileSidebarOpen: false }; // older methods handleOpenSidebar() { if (this.isMobile()) { this.openMobileSidebar(); } else { this.openSidebar(); } } openSidebar() { // Open regular sidebar } openMobileSidebar() { // Open mobile sidebar } isMobile() { // Check if mobile device } render() { // Render method } } 

تغيير صغير آخر. زوجان من الأساليب الجديدة المعروفة وخاصية جديدة.

وهنا لدينا مشكلة. لا يزال Main يؤدي وظيفة واحدة فقط (تقديم الشاشة الرئيسية) ، لكنك تنظر في جميع هذه الطرق التي نتعامل معها الآن:

 class Main extends React.Component { state = { posts: [], isSidebarOpen: false, postsToHide: [], isMobileSidebarOpen: false }; componentDidMount() { this.loadPosts(); } loadPosts() { // Load posts and save to state } handleOpenSidebar() { // Check if mobile then open relevant sidebar } handleCloseSidebar() { // Close both sidebars } openSidebar() { // Open regular sidebar } openMobileSidebar() { // Open mobile sidebar } isMobile() { // Check if mobile device } get filteredPosts() { // Return posts in state, without the postsToHide } render() { // Render method } } 

يصبح مكوننا كبيرًا وضخمًا ، ومن الصعب فهمه. ومع توسع الوظائف ، سوف يزداد الوضع سوءًا.

ما الخطأ الذي حدث؟

السبب الوحيد


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

في السابق ، قمنا بتغيير طريقة عرض المنشورات ، لذلك كان علينا تغيير المكون الرئيسي لدينا. بعد ذلك ، قمنا بتغيير طريقة فتح الشريط الجانبي - ومرة ​​أخرى قمنا بتغيير المكون الرئيسي.

يحتوي هذا المكون على العديد من الأسباب غير المرتبطة للتغيير. هذا يعني أنه يؤدي الكثير من الوظائف .

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

فصل أكثر كفاءة


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

لنبدأ من جديد. يبقى عرض الشاشة الرئيسية من مسؤولية المكون الرئيسي ، لكننا نقوم بتقليله فقط لعرض المكونات ذات الصلة:

 class Main extends React.Component { render() { return ( <Layout> <PostList /> </Layout> ); } } 

عظيم

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

دعنا ننتقل إلى Layout :

 class Layout extends React.Component { render() { return ( <SidebarDisplay> {(isSidebarOpen, toggleSidebar) => ( <div> <Header openSidebar={toggleSidebar} /> <Sidebar isOpen={isSidebarOpen} close={toggleSidebar} /> </div> )} </SidebarDisplay> ); } } 

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

نقوم بتعيين هذه الوظيفة إلى مكون SidebarDisplay ، والذي يمرر الطرق أو الحالة الضرورية إلى مكونات SidebarDisplay Sidebar .

(المثال أعلاه هو مثال لنموذج Render Props عبر الأطفال في React. إذا لم تكن معتادًا عليه ، فلا تقلق. من المهم وجود عنصر منفصل يتحكم في حالة الفتح / الإغلاق للشريط الجانبي.)

وبعد ذلك ، يمكن أن يكون Sidebar نفسه بسيطًا تمامًا إذا كان مسؤولًا فقط عن عرض الشريط الجانبي على اليمين.

 class Sidebar extends React.Component { isMobile() { // Check if mobile } render() { if (this.isMobile()) { return <MobileSidebar />; } else { return <DesktopSidebar />; } } } 

مرة أخرى ، نقاوم إغراء إدراج JSX لأجهزة الكمبيوتر / الأجهزة المحمولة مباشرةً في هذا المكون ، لأنه في هذه الحالة سيكون هناك سببان للتغيير.

دعنا ننظر إلى مكون آخر:

 class PostList extends React.Component { state = { postsToHide: [] } filterPosts(posts) { // Show posts, minus hidden ones } hidePost(post) { // Save hidden post to state } render() { return ( <PostLoader> { posts => this.filterPosts(posts).map(post => <Post />) } </PostLoader> ) } } 

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

PostLoader يتغير فقط إذا قمنا بتغيير طريقة تحميل PostLoader . وأخيرًا ، لا يتغير Post إلا إذا قمنا بتغيير طريقة عرض المنشور.

الخاتمة


كل هذه المكونات صغيرة وتؤدي وظيفة صغيرة واحدة. من السهل تحديد أسباب التغييرات فيها ، ويتم اختبار المكونات نفسها وتصحيحها.

الآن أصبح تطبيقنا أسهل في التعديل - إعادة ترتيب المكونات وإضافة وظائف جديدة وتوسيع نطاقها. تحتاج فقط إلى إلقاء نظرة على ملف مكون لتحديد ما هو عليه.

نحن نعلم أن مكوناتنا ستتغير وتنمو بمرور الوقت ، ولكن تطبيق هذه القاعدة العالمية سيساعدك على تجنب الديون الفنية وزيادة سرعة الفريق. كيفية توزيع المكونات متروك لك ، ولكن تذكر - يجب أن يكون هناك سبب واحد فقط لتغيير المكون .

شكرا لاهتمامكم ونتطلع إلى تعليقاتكم!

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


All Articles