Componenti composti in reazione

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Un componente composto è uno dei modelli avanzati di React che utilizza un modo interessante per comunicare la relazione tra i componenti dell'interfaccia utente e condividere lo stato implicito sfruttando una relazione esplicita genitore-figlio.

I componenti composti aiutano gli sviluppatori a creare API più espressive e flessibili per condividere lo stato e la logica all'interno dei componenti. Questo tutorial spiega come ciò può essere ottenuto con l'aiuto dell'API Context e React per creare componenti utilizzando questo modello avanzato.

Nota : per essere in grado di seguire, avrai bisogno di una conoscenza di base di React e di come funziona l'API Context.

Che cos'è un componente composto?

Si può dire che i componenti composti siano un modello che racchiude lo stato e il comportamento di un gruppo di componenti ma restituisce comunque il controllo del rendering delle sue parti variabili all'utente esterno.

Dalla definizione di cui sopra, prendendo atto delle parole chiave: stato e comportamento . Questo ci aiuta a capire che il componente composto ha a che fare con lo stato (cioè come si comporta lo stato in un componente che è racchiuso da un utente esterno che è il genitore del componente).

L'obiettivo dei componenti composti è fornire un'API più espressiva e flessibile per la comunicazione tra i componenti padre e figlio.

Pensalo come i <select> e <option> in HTML:

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

Il tag select funziona insieme al tag option che viene utilizzato per un menu a discesa per selezionare gli elementi in HTML. Qui <select> gestisce lo stato dell'interfaccia utente, quindi gli elementi <option> sono configurati su come dovrebbe funzionare <select> . I componenti composti in React vengono utilizzati per creare un componente dichiarativo dell'interfaccia utente che aiuta a evitare la perforazione dell'elica.

La perforazione dell'elica sta passando oggetti di scena su più componenti figlio. Questo è anche ciò che chiamano "odore di codice". La parte peggiore della perforazione dell'elica è che quando il componente padre esegue il rendering di nuovo, anche i componenti figlio verranno visualizzati nuovamente e causeranno un effetto domino sul componente. Una buona soluzione sarebbe utilizzare l'API React Context che esamineremo anche in seguito.

Altro dopo il salto! Continua a leggere sotto ↓

Applicazione di componenti composti in reazione

Questa sezione spiega i pacchetti che possiamo utilizzare nella nostra applicazione che adottano lo schema dei componenti composti dei componenti dell'edificio in React. Questo esempio è un componente Menu del pacchetto @reach UI.

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

Ecco un modo per utilizzare il componente Menu :

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

Il codice di esempio sopra è una delle implementazioni di componenti composti in cui puoi vedere che Menu , MenuButton , MenuList , MenuItem e MenuLink sono stati tutti importati da @reach/menu-button . Invece di esportare un singolo componente, ReachUI esporta un componente padre che è Menu che accompagna i suoi componenti figli che sono MenuButton , MenuList , MenuItem e MenuLink .

Quando dovresti usare i componenti composti?

In qualità di sviluppatore React, dovresti utilizzare componenti composti quando desideri:

  • Risolvi i problemi relativi alla costruzione di componenti riutilizzabili;
  • Sviluppo di componenti ad alta coesione con accoppiamento minimo;
  • Modi migliori per condividere la logica tra i componenti.

Pro e contro dei componenti composti

Un componente composto è un fantastico pattern React da aggiungere al tuo toolkit per sviluppatori React. In questa sezione, illustrerò i pro ei contro dell'utilizzo di componenti composti e ciò che ho imparato dalla creazione di componenti utilizzando questo modello di sviluppo.

Professionisti

  • Separazione delle preoccupazioni
    Avere tutta la logica dello stato dell'interfaccia utente nel componente padre e comunicarla internamente a tutti i componenti figlio crea una chiara divisione delle responsabilità.

  • Complessità ridotta
    A differenza della perforazione dell'elica per trasmettere le proprietà ai loro componenti specifici, gli oggetti di scena figlio vanno ai rispettivi componenti figlio utilizzando il modello del componente composto.

contro

Uno dei principali svantaggi della costruzione di componenti in React con il pattern di componenti composti è che solo direct children del componente genitore avranno accesso agli oggetti di scena, il che significa che non possiamo avvolgere nessuno di questi componenti in un altro componente.

 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> ); }

Una soluzione a questo problema sarebbe utilizzare il modello di componente composto flessibile per condividere implicitamente lo stato utilizzando l'API React.createContext .

L'API Context consente di passare lo stato di React attraverso i componenti nidificati durante la costruzione utilizzando il modello di componente composto dei componenti di costruzione in React. Ciò è possibile perché il context fornisce un modo per trasmettere i dati all'albero dei componenti senza dover passare manualmente gli oggetti di scena a tutti i livelli. L'utilizzo dell'API Context offre molta flessibilità all'utente finale.

Mantenere i componenti composti in reazione

I componenti composti forniscono un modo più flessibile per condividere lo stato all'interno delle applicazioni React, quindi l'utilizzo di componenti composti nelle tue applicazioni React semplifica la manutenzione e il debug effettivo delle tue app.

Costruire una demo

In questo articolo, costruiremo un componente a fisarmonica in React usando il pattern dei componenti composti. Il componente che creeremo in questo tutorial sarebbe un componente a fisarmonica personalizzato che è flessibile e condivide lo stato all'interno del componente utilizzando l'API Context.

Andiamo!

Prima di tutto, creiamo un'app React utilizzando quanto segue:

 npx create-react-app accordionComponent cd accordionComponent npm start

o

 yarn create react-app accordionComponent cd accordionComponent yarn start

I comandi precedenti creano un'app React, cambiano la directory nel progetto React e avviano il server di sviluppo.

Nota : in questo tutorial, utilizzeremo styled-components con stile per aiutare a modellare i nostri componenti.

Utilizzare il comando seguente per installare styled-components :

 yarn add styled-components

o

 npm install --save styled-components

Nella cartella src , crea una nuova cartella chiamata components . È qui che vivrebbero tutti i nostri componenti. All'interno della cartella dei componenti , crea due nuovi file: accordion.js e accordion.styles.js .

Il file accordion.styles.js contiene il nostro stile per il componente Accordion (il nostro stile è stato eseguito utilizzando styled-components ).

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

Sopra è riportato un esempio di componenti di stile che utilizzano la libreria css-in-js denominata styled-components .

All'interno del file accordion.styles.js , aggiungi gli stili rimanenti:

 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; } `;

Iniziamo a costruire il nostro componente per fisarmonica. Nel file accordion.js , aggiungiamo il seguente codice:

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

Sopra, stiamo importando gli useState , useContext e createContext che ci aiuteranno a costruire il nostro componente a fisarmonica usando componenti composti.

La documentazione di React spiega che il context aiuta a fornire un modo per passare i dati attraverso l'albero dei componenti senza dover passare manualmente gli oggetti di scena a tutti i livelli.

Osservando ciò che abbiamo importato in precedenza nel nostro file accordion.js , noterai che abbiamo anche importato i nostri stili come componenti che ci aiuteranno a costruire i nostri componenti più velocemente.

Andremo avanti e creeremo il nostro contesto per il componente che condividerà i dati con i componenti che ne hanno bisogno:

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

Il Container e i componenti Inner dello snippet di codice sopra provengono dal nostro file ./accordion.styles.js in cui abbiamo creato gli stili per i nostri componenti utilizzando gli styled-components (dalla libreria css-in-js ). Il componente Container ospita l'intera Accordion che stiamo costruendo utilizzando componenti compositi.

Qui stiamo creando un oggetto di contesto utilizzando il metodo createContext() , quindi quando React esegue il rendering di un componente che si iscrive a questo oggetto di contesto, leggerà il valore di contesto corrente dal provider corrispondente più vicino sopra di esso nell'albero.

Quindi stiamo creando anche il nostro componente di base che è la Fisarmonica; ci vogliono i children e qualsiasi restProps . Questo è il nostro componente genitore che ospita i componenti figli della Fisarmonica.

Creiamo altri componenti figli all'interno del file accordion.js :

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

Notare il . dopo il componente fisarmonica madre; questo viene utilizzato per connettere il componente figlio al suo componente padre.

Continuiamo. Ora aggiungi quanto segue al file 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> ); };

Quindi qui stiamo creando un componente Body , Header e Item che sono tutti figli del componente padre Accordion . È qui che potrebbe iniziare a diventare complicato. Inoltre, nota che ogni componente figlio creato qui riceve anche un prop children e restprops .

Dal componente figlio Item , abbiamo inizializzato il nostro stato usando l'hook useState e lo abbiamo impostato su true. Quindi ricorda anche che abbiamo creato un ToggleContext al livello più alto del file accordion.js che è un Context Object e quando React esegue il rendering di un componente che si iscrive a questo oggetto Context, leggerà il valore del contesto corrente dal Provider corrispondente più vicino sopra di esso nell'albero.

Ogni oggetto Context viene fornito con un componente Provider React che consente ai componenti di consumo di sottoscrivere le modifiche al contesto.

Il componente provider accetta un value prop da passare ai componenti di consumo che sono discendenti di questo provider, e qui stiamo passando il valore dello stato corrente che è il toggleShow e il metodo per impostare il valore dello stato corrente setToggleShow . Sono il valore che determina in che modo il nostro oggetto contesto condividerà lo stato attorno al nostro componente senza perforazione dell'elica.

Quindi nel nostro componente figlio di header di Accordion , stiamo distruggendo i valori dell'oggetto contesto, quindi cambiamo lo stato corrente di toggleShow al clic. Quindi quello che stiamo cercando di fare è nascondere o mostrare la nostra fisarmonica quando si fa clic sull'intestazione.

Nel nostro componente Accordion.Body , stiamo anche distruggendo toggleShow che è lo stato corrente del componente, quindi, a seconda del valore di toggleShow , possiamo nascondere il corpo o mostrare il contenuto del componente Accordion.Body .

Quindi è tutto per il nostro file accordion.js .

Ora è qui che possiamo vedere come tutto ciò che abbiamo appreso sui componenti Context e Compound components si combinano. Ma prima, creiamo un nuovo file chiamato data.json e incolliamo il contenuto qui sotto:

 [ { "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." } ]

Questi sono i dati con cui lavoreremo per testare il nostro componente a fisarmonica.

Quindi andiamo avanti. Abbiamo quasi finito e credo che tu abbia imparato molto seguendo questo articolo.

In questa sezione, riuniremo tutto ciò su cui abbiamo lavorato e appreso sui componenti composti per poterlo utilizzare nel nostro file App.js per utilizzare la funzione Array.map per visualizzare i dati che già abbiamo sul web pagina. Si noti inoltre che non è stato utilizzato lo stato all'interno di App.js ; tutto ciò che abbiamo fatto è stato trasmettere i dati ai componenti specifici e l'API Context si è occupata di ogni altra cosa.

Ora passiamo alla parte finale. Nel tuo App.js , procedi come segue:

 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> ); }

Nel tuo file App.js , abbiamo importato la nostra fisarmonica componente composta dal percorso del file, quindi abbiamo anche importato i nostri dati fittizi, mappati attraverso i dati fittizi per ottenere i singoli elementi nel nostro file di dati, quindi li abbiamo visualizzati in conformità con il rispettivo componente, inoltre, noterai che tutto ciò che dovevamo fare era passare i figli al rispettivo componente, l'API Context si occupa di garantire che raggiunga il componente giusto e che non vi fosse alcuna perforazione dell'elica.

Ecco come dovrebbe essere il nostro prodotto finale:

Aspetto finale del nostro componente per fisarmonica
Aspetto finale del nostro componente per fisarmonica. (Grande anteprima)

Alternativa ai componenti composti

Un'alternativa all'utilizzo di componenti composti sarebbe utilizzare l'API Render Props. Il termine Render Prop in React si riferisce a una tecnica per condividere il codice tra i componenti di React utilizzando un prop il cui valore è una funzione. Un componente con un prop di rendering prende una funzione che restituisce un elemento React e lo chiama invece di implementare la propria logica di rendering.

Il passaggio dei dati da un componente a un componente figlio che necessita dei dati può comportare il drill puntello quando i componenti sono nidificati l'uno nell'altro. Questo è il vantaggio dell'utilizzo di Context per condividere i dati tra i componenti rispetto all'utilizzo del metodo render prop.

Conclusione

In questo articolo, abbiamo appreso di uno dei pattern avanzati di React che è il pattern dei componenti composti. È un metodo fantastico per costruire componenti riutilizzabili in React utilizzando il modello di componente composto per costruire il tuo componente ti offre molta flessibilità nel tuo componente. Puoi comunque scegliere di utilizzare Render Prop se la flessibilità non è ciò che il tuo componente richiede al momento.

I componenti composti sono più utili nei sistemi di progettazione degli edifici. Abbiamo anche esaminato il processo di condivisione dello stato all'interno dei componenti utilizzando l'API Context.

  • Il codice per questo tutorial può essere trovato su Codesandbox.