المكونات المركبة في React

نشرت: 2022-03-10
ملخص سريع ↬ يعد المكون المركب أحد الأنماط المتقدمة لـ React التي تستخدم طريقة مثيرة للاهتمام لتوصيل العلاقة بين مكونات واجهة المستخدم ومشاركة الحالة الضمنية من خلال الاستفادة من علاقة صريحة بين الوالدين والطفل.

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

ملاحظة : لكي تكون قادرًا على المتابعة ، ستحتاج إلى فهم أساسي لـ React وكيفية عمل Context API.

ما هو المكون المركب؟

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

من التعريف أعلاه ، لاحظ الكلمات الرئيسية: الحالة والسلوك . يساعدنا هذا في فهم أن المكون المركب يتعامل مع الحالة (أي كيف تتصرف الحالة عبر مكون محاط بمستخدم خارجي هو أصل المكون).

الهدف من المكونات المركبة هو توفير واجهة برمجة تطبيقات أكثر تعبيراً ومرونة للتواصل بين المكونات الرئيسية والمكونات الفرعية.

فكر في الأمر مثل علامتي <select> و <option> في HTML:

 <select> <option value="volvo">Volvo</option> <option value="mercedes">Mercedes</option> <option value="audi">Audi</option> </select>

تعمل علامة select مع علامة option المستخدمة في القائمة المنسدلة لتحديد العناصر بتنسيق HTML. هنا يدير <select> حالة واجهة المستخدم ، ثم يتم تكوين عناصر <option> حول كيفية عمل <select> . تُستخدم المكونات المركبة في React لبناء مكون واجهة مستخدم تعريفي يساعد على تجنب الحفر الداعم.

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

المزيد بعد القفز! أكمل القراءة أدناه ↓

تطبيق المكونات المركبة في React

يشرح هذا القسم الحزم التي يمكننا الاستفادة منها في تطبيقنا والتي تتبنى نمط المكون المركب لمكونات البناء في React. هذا المثال هو أحد مكونات Menu من حزمة @reach UI.

 import { Menu, MenuList, MenuButton, MenuItem, MenuItems, MenuPopover, MenuLink, } from "@reach/menu-button"; import "@reach/menu-button/styles.css";

إليك طريقة يمكنك من خلالها استخدام مكون Menu :

 function Example() { return ( <Menu> <MenuButton>Actions</MenuButton> <MenuList> <MenuItem>Download</MenuItem> <MenuLink to="view">View</MenuLink> </MenuList> </Menu> ); }

رمز المثال أعلاه هو أحد تطبيقات المكونات المركبة التي يمكنك من خلالها رؤية أن Menu ، MenuButton ، MenuList ، MenuItem و MenuLink تم استيرادها جميعًا من @reach/menu-button . على عكس تصدير مكون واحد ، تقوم ReachUI بتصدير المكون الرئيسي وهو Menu المصاحبة للمكونات الفرعية الخاصة به والتي هي MenuButton و MenuList و MenuItem و MenuLink .

متى يجب عليك استخدام المكونات المركبة؟

بصفتك مطور React ، يجب عليك الاستفادة من المكونات المركبة عندما تريد:

  • حل المشكلات المتعلقة ببناء المكونات القابلة لإعادة الاستخدام ؛
  • تطوير مكونات عالية التماسك بأقل اقتران ؛
  • أفضل الطرق لمشاركة المنطق بين المكونات.

إيجابيات وسلبيات المكونات المركبة

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

الايجابيات

  • فصل القلق
    إن وجود كل منطق حالة واجهة المستخدم في المكون الرئيسي وإيصال ذلك داخليًا إلى جميع المكونات الفرعية يجعل تقسيمًا واضحًا للمسؤولية.

  • تعقيد أقل
    على عكس الحفر الداعم لتمرير الخصائص إلى مكوناتها المحددة ، تنتقل الدعائم الفرعية إلى المكونات الفرعية الخاصة بها باستخدام نمط المكون المركب.

سلبيات

تتمثل إحدى السلبيات الرئيسية لمكونات البناء في React باستخدام نمط المكون المركب في أن العناصر الفرعية direct children للمكوِّن الأصلي فقط هي التي ستتمكن من الوصول إلى الدعائم ، مما يعني أنه لا يمكننا تغليف أي من هذه المكونات في مكون آخر.

 export default function FlyoutMenu() { return ( <FlyOut> {/* This breaks */} <div> <FlyOut.Toggle /> <FlyOut.List> <FlyOut.Item>Edit</FlyOut.Item> <FlyOut.Item>Delete</FlyOut.Item> </FlyOut.List> </div> </FlyOut> ); }

قد يكون حل هذه المشكلة هو استخدام نمط المكون المركب المرن لمشاركة الحالة ضمنيًا باستخدام React.createContext API.

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

الحفاظ على المكونات المركبة في React

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

بناء تجريبي

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

دعنا نذهب!

بادئ ذي بدء ، لنقم بإنشاء تطبيق React باستخدام ما يلي:

 npx create-react-app accordionComponent cd accordionComponent npm start

أو

 yarn create react-app accordionComponent cd accordionComponent yarn start

الأوامر أعلاه تنشئ تطبيق React ، وتغير الدليل إلى مشروع React ، وتبدأ تشغيل خادم التطوير.

ملاحظة : في هذا البرنامج التعليمي ، سنستخدم styled-components المصممة للمساعدة في تصميم مكوناتنا.

استخدم الأمر أدناه لتثبيت styled-components :

 yarn add styled-components

أو

 npm install --save styled-components

في المجلد src ، أنشئ مجلدًا جديدًا يسمى المكونات . هذا هو المكان الذي ستعيش فيه جميع مكوناتنا. داخل مجلد المكونات ، أنشئ ملفين جديدين: accordion.js و accordion.styles.js .

يحتوي ملف Accordion accordion.styles.js تم تصميمنا باستخدام styled-components ).

 import styled from "styled-components"; export const Container = styled.div` display: flex; border-bottom: 8px solid #222; `;

أعلاه هو مثال لمكونات التصميم باستخدام مكتبة css-in-js تسمى styled-components .

في ملف accordion.styles.js ، أضف الأنماط المتبقية:

 export const Frame = styled.div` margin-bottom: 40px; `; export const Inner = styled.div` display: flex; padding: 70px 45px; flex-direction: column; max-width: 815px; margin: auto; `; export const Title = styled.h1` font-size: 40px; line-height: 1.1; margin-top: 0; margin-bottom: 8px; color: black; text-align: center; `; export const Item = styled.div` color: white; margin: auto; margin-bottom: 10px; max-width: 728px; width: 100%; &:first-of-type { margin-top: 3em; } &:last-of-type { margin-bottom: 0; } `; export const Header = styled.div` display: flex; flex-direction: space-between; cursor: pointer; margin-bottom: 1px; font-size: 26px; font-weight: normal; background: #303030; padding: 0.8em 1.2em 0.8em 1.2em; user-select: none; align-items: center; img { filter: brightness(0) invert(1); width: 24px; user-select: none; @media (max-width: 600px) { width: 16px; } } `; export const Body = styled.div` font-size: 26px; font-weight: normal; line-height: normal; background: #303030; white-space: pre-wrap; user-select: none; overflow: hidden; &.closed { max-height: 0; overflow: hidden; transition: max-height 0.25ms cubic-bezier(0.5, 0, 0.1, 1); } &.open { max-height: 0px; transition: max-height 0.25ms cubic-bezier(0.5, 0, 0.1, 1); } span { display: block; padding: 0.8em 2.2em 0.8em 1.2em; } `;

لنبدأ في بناء مكون الأكورديون الخاص بنا. في ملف accordion.js ، دعنا نضيف الكود التالي:

 import React, { useState, useContext, createContext } from "react"; import { Container, Inner, Item, Body, Frame, Title, Header } from "./accordion.styles";

أعلاه ، useState useState و useContext و createContext hooks والتي ستساعدنا في بناء مكون الأكورديون الخاص بنا باستخدام مكونات مركبة.

توضح وثائق React أن context يساعد في توفير طريقة لتمرير البيانات عبر شجرة المكوّنات دون الحاجة إلى تمرير الخاصيّات يدويًا على كل مستوى.

بالنظر إلى ما قمنا باستيراده مسبقًا في ملف accordion.js بنا ، ستلاحظ أننا قمنا أيضًا باستيراد أنماطنا كمكونات ستساعدنا في بناء مكوناتنا بشكل أسرع.

سنمضي قدمًا وننشئ سياقنا للمكون الذي سيشارك البيانات مع المكونات التي تحتاجها:

 const ToggleContext = createContext(); export default function Accordion({ children, ...restProps }) { return ( <Container {...restProps}> <Inner>{children}</Inner> </Container> ); }

Container والمكونات Inner من مقتطف الشفرة أعلاه مأخوذة من ملف ./accordion.styles.js بنا حيث أنشأنا أنماطًا لمكوناتنا باستخدام المكونات styled-components (من مكتبة css-in-js ). يضم مكون Container Accordion بالكامل الذي نقوم ببنائه باستخدام مكونات مركبة.

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

ثم نقوم أيضًا بإنشاء المكون الأساسي الخاص بنا وهو الأكورديون ؛ يأخذ children وأي restProps . هذا هو المكون الرئيسي الذي يحتوي على مكونات الأطفال في الأكورديون.

لنقم بإنشاء مكونات فرعية أخرى داخل ملف accordion.js :

 Accordion.Title = function AccordionTitle({ children, ...restProps }) { return <Title {...restProps}>{children}</Title>; }; Accordion.Frame = function AccordionFrame({ children, ...restProps }) { return <Frame {...restProps}>{children}</Frame>; };

لاحظ . بعد مكون الأكورديون الأصل ؛ يستخدم هذا لتوصيل المكون الفرعي بمكونه الأصلي.

لنكمل. أضف الآن ما يلي إلى ملف accordion.js :

 Accordion.Item = function AccordionItem({ children, ...restProps }) { const [toggleShow, setToggleShow] = useState(true); return ( <ToggleContext.Provider value={{ toggleShow, setToggleShow }}> <Item {...restProps}>{children}</Item> </ToggleContext.Provider> ); }; Accordion.ItemHeader = function AccordionHeader({ children, ...restProps }) { const { isShown, toggleIsShown } = useContext(ToggleContext); return ( <Header onClick={() => toggleIsShown(!isShown)} {...restProps}> {children} </Header> ); }; Accordion.Body = function AccordionHeader({ children, ...restProps }) { const { isShown } = useContext(ToggleContext); return ( <Body className={isShown ? "open" : "close"}> <span>{children}</span> </Body> ); };

لذلك نحن هنا بصدد إنشاء مكون Body و Header و Item وجميعها أبناء المكون الرئيسي Accordion . هذا هو المكان الذي قد يبدأ فيه التعقيد. لاحظ أيضًا أن كل مكون فرعي تم إنشاؤه هنا يتلقى أيضًا دعامة children و restprops .

من المكوِّن الفرعي Item ، بدأنا حالتنا باستخدام الخطاف useState على صواب. ثم تذكر أيضًا أننا أنشأنا ToggleContext في المستوى العلوي من ملف accordion.js وهو عبارة عن Context Object ، وعندما تعرض React مكونًا يشترك في كائن السياق هذا ، ستقرأ قيمة السياق الحالية من أقرب موفر مطابق أعلاه. في الشجرة.

يأتي كل كائن سياق مع مكون Provider React الذي يسمح باستهلاك المكونات للاشتراك في تغييرات السياق.

يقبل مكوِّن provider خاصية value ليتم تمريرها إلى المكونات المستهلكة التي تنحدر من هذا الموفر ، وهنا نقوم بتمرير قيمة الحالة الحالية وهي toggleShow وطريقة لتعيين قيمة الحالة الحالية setToggleShow . إنها القيمة التي تحدد كيفية مشاركة كائن السياق الخاص بنا في الحالة حول المكون الخاص بنا دون التنقيب عن العناصر.

ثم في مكون header الفرعي الخاص بنا في Accordion ، نقوم بتدمير قيم كائن السياق ، ثم تغيير الحالة الحالية لـ toggleShow عند النقر. لذا فإن ما نحاول القيام به هو إخفاء أو إظهار الأكورديون الخاص بنا عند النقر فوق الرأس.

في مكون Accordion.Body الخاص بنا ، نقوم أيضًا بتدمير toggleShow وهو الحالة الحالية للمكون ، ثم اعتمادًا على قيمة toggleShow ، يمكننا إما إخفاء الجسم أو إظهار محتويات مكون Accordion.Body .

هذا كل ما يخص ملف accordion.js الخاص بنا.

الآن هذا هو المكان الذي نتمكن فيه من رؤية كيف أن كل شيء تعلمناه عن Compound components Context والمركب يجتمعان معًا. ولكن قبل ذلك ، لنقم بإنشاء ملف جديد يسمى data.json ولصق المحتوى أدناه فيه:

 [ { "id": 1, "header": "What is Netflix?", "body": "Netflix is a streaming service that offers a wide variety of award-winning TV programs, films, anime, documentaries and more – on thousands of internet-connected devices.\n\nYou can watch as much as you want, whenever you want, without a single advert – all for one low monthly price. There's always something new to discover, and new TV programs and films are added every week!" }, { "id": 2, "header": "How much does Netflix cost?", "body": "Watch Netflix on your smartphone, tablet, smart TV, laptop or streaming device, all for one low fixed monthly fee. Plans start from £5.99 a month. No extra costs or contracts." }, { "id": 3, "header": "Where can I watch?", "body": "Watch anywhere, anytime, on an unlimited number of devices. Sign in with your Netflix account to watch instantly on the web at netflix.com from your personal computer or on any internet-connected device that offers the Netflix app, including smart TVs, smartphones, tablets, streaming media players and game consoles.\n\nYou can also download your favorite programs with the iOS, Android, or Windows 10 app. Use downloads to watch while you're on the go and without an internet connection. Take Netflix with you anywhere." }, { "id": 4, "header": "How do I cancel?", "body": "Netflix is flexible. There are no annoying contracts and no commitments. You can easily cancel your account online with two clicks. There are no cancellation fees – start or stop your account at any time." }, { "id": 5, "header": "What can I watch on Netflix?", "body": "Netflix has an extensive library of feature films, documentaries, TV programs, anime, award-winning Netflix originals, and more. Watch as much as you want, any time you want." } ]

هذه هي البيانات التي سنعمل معها لاختبار مكون الأكورديون الخاص بنا.

لذلك دعونا نستمر. لقد أوشكنا على الانتهاء وأعتقد أنك قد تعلمت الكثير من متابعة هذه المقالة.

في هذا القسم ، سنجمع كل ما كنا نعمل عليه ونتعلم عن المكونات المركبة حتى نتمكن من استخدامها في ملف App.js بنا لاستخدام وظيفة Array.map لعرض البيانات التي لدينا بالفعل على الويب صفحة. لاحظ أيضًا أنه لم يكن هناك استخدام للحالة داخل App.js ؛ كل ما فعلناه هو تمرير البيانات إلى مكونات محددة وتولى السياق API كل شيء آخر.

الآن إلى الجزء الأخير. في App.js الخاص بك ، قم بما يلي:

 import React from "react"; import Accordion from "./components/Accordion"; import faqData from "./data"; export default function App() { return ( <Accordion> <Accordion.Title>Frequently Asked Questions</Accordion.Title> <Accordion.Frame> {faqData.map((item) => ( <Accordion.Item key={item.id}> <Accordion.Header>{item.header}</Accordion.Header> <Accordion.Body>{item.body}</Accordion.Body> </Accordion.Item> ))} </Accordion.Frame> </Accordion> ); }

في ملف App.js الخاص بك ، قمنا باستيراد الأكورديون المركب الخاص بنا من مسار الملف ، ثم قمنا أيضًا باستيراد بياناتنا الوهمية ، وتعيينها من خلال البيانات الوهمية من أجل الحصول على العناصر الفردية في ملف البيانات الخاص بنا ، ثم عرضها وفقًا لكل منها المكون ، ستلاحظ أيضًا أن كل ما كان علينا فعله هو تمرير العناصر الفرعية إلى المكون المعني ، وتهتم واجهة برمجة تطبيقات السياق بضمان وصولها إلى المكون الصحيح وعدم وجود حفر دعامة.

هذا ما يجب أن يبدو عليه منتجنا النهائي:

الشكل النهائي لمكون الأكورديون الخاص بنا
الشكل النهائي لمكون الأكورديون الخاص بنا. (معاينة كبيرة)

بديل للمكونات المركبة

قد يكون البديل لاستخدام المكونات المركبة هو الاستفادة من واجهة برمجة تطبيقات Render Props. يشير المصطلح Render Prop في React إلى تقنية لمشاركة التعليمات البرمجية بين مكونات React باستخدام خاصية الخاصية التي تكون قيمتها دالة. يأخذ المكوِّن الذي له خاصية تصيير دالة تقوم بإرجاع عنصر React واستدعائه بدلاً من تطبيق منطق التصيير الخاص به.

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

خاتمة

في هذه المقالة ، تعرفنا على أحد الأنماط المتقدمة لـ React وهو نمط المكون المركب. إنها طريقة رائعة لبناء مكونات قابلة لإعادة الاستخدام في React باستخدام نمط المكون المركب لبناء المكون الخاص بك ، مما يوفر لك الكثير من المرونة في المكون الخاص بك. لا يزال بإمكانك اختيار استخدام Render Prop إذا لم تكن المرونة هي ما يتطلبه المكون الخاص بك في الوقت الحالي.

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

  • يمكن العثور على رمز هذا البرنامج التعليمي على Codesandbox.