Come utilizzare i componenti stilizzati in React

Pubblicato: 2022-03-10
Breve riassunto ↬ Sebbene l'approccio basato sui componenti abbia inaugurato una nuova frontiera nel modo in cui costruiamo le applicazioni web, non è privo di imperfezioni, una delle quali è la sua usabilità e scalabilità con CSS. Questo ha dato vita a un nuovo modo di costruire e gestire i nostri stili in un modo specifico per i componenti , altrimenti noto come CSS-in-JS.

I componenti stilizzati sono uno strumento CSS-in-JS che colma il divario tra i componenti e lo stile, offrendo numerose funzionalità per iniziare a utilizzare lo stile dei componenti in modo funzionale e riutilizzabile. In questo articolo imparerai le basi dei componenti stilizzati e come applicarli correttamente alle tue applicazioni React. Avresti dovuto lavorare su React in precedenza prima di seguire questo tutorial. Se stai cercando varie opzioni nello styling dei componenti React, puoi dare un'occhiata al nostro post precedente sull'argomento.

Al centro dei CSS c'è la capacità di indirizzare qualsiasi elemento HTML, a livello globale, indipendentemente dalla sua posizione nell'albero DOM. Questo può essere un ostacolo quando vengono utilizzati con i componenti, perché i componenti richiedono, in misura ragionevole, la colocation (cioè il mantenimento di risorse come stati e stile) più vicino al luogo in cui vengono utilizzati (noto come localizzazione).

Nelle stesse parole di React, i componenti con stile sono " primitive visive per i componenti " e il loro obiettivo è quello di darci un modo flessibile per dare uno stile ai componenti. Il risultato è uno stretto accoppiamento tra i componenti ei loro stili.

Nota: i componenti con stile sono disponibili sia per React che per React Native, e mentre dovresti assolutamente dare un'occhiata alla guida React Native, il nostro focus qui sarà sui componenti con stile per React.

Altro dopo il salto! Continua a leggere sotto ↓

Perché i componenti in stile?

Oltre ad aiutarti nell'ambito degli stili, i componenti con stili includono le seguenti funzionalità:

  • Prefisso fornitore automatico
    È possibile utilizzare le proprietà CSS standard e i componenti con stile aggiungeranno i prefissi del fornitore qualora fossero necessari.
  • Nomi di classi univoci
    I componenti stilizzati sono indipendenti l'uno dall'altro e non devi preoccuparti dei loro nomi perché la libreria lo gestisce per te.
  • Eliminazione degli stili morti
    I componenti con stile rimuovono gli stili inutilizzati, anche se sono dichiarati nel codice.
  • e tanti altri.

Installazione

L'installazione di componenti in stile è facile. Puoi farlo tramite una CDN o con un gestore di pacchetti come Yarn...

 yarn add styled-components

… o npm:

 npm i styled-components

La nostra demo utilizza create-react-app.

Inziando

Forse la prima cosa che noterai dei componenti con stile è la loro sintassi, che può essere scoraggiante se non capisci la magia dietro i componenti con stile. Per dirla in breve, i componenti con stile utilizzano i valori letterali del modello di JavaScript per colmare il divario tra componenti e stili. Quindi, quando crei un componente con stili, quello che stai effettivamente creando è un componente React con stili. Si presenta così:

 import styled from "styled-components"; // Styled component named StyledButton const StyledButton = styled.button` background-color: black; font-size: 32px; color: white; `; function Component() { // Use it like any other component. return <StyledButton> Login </StyledButton>; }

Qui, StyledButton è il componente con stile e verrà visualizzato come un pulsante HTML con gli stili contenuti. styled è un metodo di utilità interno che trasforma lo stile da JavaScript in CSS effettivo.

In HTML e CSS non elaborati, avremmo questo:

 button { background-color: black; font-size: 32px; color: white; } <button> Login </button>

Se i componenti con stile sono componenti React, possiamo usare gli oggetti di scena? Sì possiamo.

Adattamento basato su oggetti di scena

I componenti stilizzati sono funzionali , quindi possiamo facilmente modellare gli elementi in modo dinamico. Supponiamo di avere due tipi di pulsanti sulla nostra pagina, uno con uno sfondo nero e l'altro blu. Non è necessario creare due componenti in stile per loro; possiamo adattare il loro stile in base ai loro oggetti di scena.

 import styled from "styled-components"; const StyledButton = styled.button` min-width: 200px; border: none; font-size: 18px; padding: 7px 10px; /* The resulting background color will be based on the bg props. */ background-color: ${props => props.bg === "black" ? "black" : "blue"; `; function Profile() { return ( <div> <StyledButton bg="black">Button A</StyledButton> <StyledButton bg="blue">Button B</StyledButton> </div> ) }

Poiché StyledButton è un componente React che accetta oggetti di scena, possiamo assegnare un colore di sfondo diverso in base all'esistenza o al valore del prop bg .

Noterai, tuttavia, che non abbiamo assegnato un type al nostro pulsante . Facciamolo:

 function Profile() { return ( <> <StyledButton bg="black" type="button"> Button A </StyledButton> <StyledButton bg="blue" type="submit" onClick={() => alert("clicked")}> Button B </StyledButton> </> ); }

I componenti stilizzati possono distinguere tra i tipi di oggetti di scena che ricevono. Sanno che type è un attributo HTML, quindi in realtà rendono <button type="button">Button A</button> , mentre usano il prop bg nella propria elaborazione. Hai notato come abbiamo collegato anche un gestore di eventi?

Parlando di attributi, una sintassi estesa ci consente di gestire gli oggetti di scena usando il costruttore attrs . Controllalo:

 const StyledContainer = styled.section.attrs((props) => ({ width: props.width || "100%", hasPadding: props.hasPadding || false, }))` --container-padding: 20px; width: ${(props) => props.width}; // Falls back to 100% padding: ${(props) => (props.hasPadding && "var(--container-padding)") || "none"}; `;

Nota come non abbiamo bisogno di un ternario quando impostiamo la larghezza? Questo perché abbiamo già impostato un valore predefinito con width: props.width || "100%", width: props.width || "100%", . Inoltre, abbiamo utilizzato le proprietà personalizzate CSS perché possiamo!

Nota: se i componenti con stile sono componenti React e possiamo passare gli oggetti di scena, allora possiamo usare anche gli stati? L'account GitHub della libreria ha un problema che affronta proprio questo problema.

Estendere gli stili

Diciamo che stai lavorando su una pagina di destinazione e che hai impostato il tuo contenitore su una certa larghezza massima per mantenere le cose centrate. Hai uno StyledContainer per questo:

 const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `;

Quindi, scopri che hai bisogno di un contenitore più piccolo, con un riempimento di 10 pixel su entrambi i lati, invece di 20 pixel. Il tuo primo pensiero potrebbe essere quello di creare un altro componente con uno stile, e avresti ragione, ma non ci vorrà molto tempo prima che ti rendi conto che stai duplicando gli stili.

 const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `; const StyledSmallContainer = styled.section` max-width: 1024px; padding: 0 10px; margin: 0 auto; `;

Prima di procedere e creare StyledSmallContainer , come nello snippet sopra, impariamo come riutilizzare ed ereditare gli stili. È più o meno come funziona l'operatore di spread :

 const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `; // Inherit StyledContainer in StyledSmallConatiner const StyledSmallContainer = styled(StyledContainer)` padding: 0 10px; `; function Home() { return ( <StyledContainer> <h1>The secret is to be happy</h1> </StyledContainer> ); } function Contact() { return ( <StyledSmallContainer> <h1>The road goes on and on</h1> </StyledSmallContainer> ); }

Nel tuo StyledSmallContainer , otterrai tutti gli stili da StyledContainer , ma il riempimento verrà sovrascritto. Tieni presente che, di solito, otterrai un elemento di sezione renderizzato per StyledSmallContainer , perché è ciò che esegue il rendering di StyledContainer . Ma ciò non significa che sia scolpito nella pietra o immutabile.

Il "come" Prop. Polimorfico

Con il prop as polimorfico, puoi scambiare l'elemento finale che viene renderizzato. Un caso d'uso è quando si ereditano gli stili (come nell'ultimo esempio). Se, ad esempio, preferisci un div a una section per StyledSmallContainer , puoi passare as prop al tuo componente con stile con il valore del tuo elemento preferito, in questo modo:

 function Home() { return ( <StyledContainer> <h1>It's business, not personal</h1> </StyledContainer> ); } function Contact() { return ( <StyledSmallContainer as="div"> <h1>Never dribble when you can pass</h1> </StyledSmallContainer> ); }

Ora, StyledSmallContainer verrà renderizzato come div . Potresti anche avere un componente personalizzato come valore:

 function Home() { return ( <StyledContainer> <h1>It's business, not personal</h1> </StyledContainer> ); } function Contact() { return ( <StyledSmallContainer as={StyledContainer}> <h1>Never dribble when you can pass</h1> </StyledSmallContainer> ); }

Non darlo per scontato.

Sintassi simile a SCSS

Il preprocessore CSS Stylis consente ai componenti con stile di supportare la sintassi simile a SCSS, come la nidificazione:

 const StyledProfileCard = styled.div` border: 1px solid black; > .username { font-size: 20px; color: black; transition: 0.2s; &:hover { color: red; } + .dob { color: grey; } } `; function ProfileCard() { return ( <StyledProfileCard> <h1 className="username">John Doe</h1> <p className="dob"> Date: <span>12th October, 2013</span> </p> <p className="gender">Male</p> </StyledProfileCard> ); }

Animazione

I componenti con stile hanno un supporto per i keyframes che aiuta a costruire fotogrammi chiave di animazione (riutilizzabili). Il vantaggio qui è che i fotogrammi chiave verranno staccati dai componenti stilizzati e possono essere esportati e riutilizzati dove necessario.

 import styled, {keyframes} from "styled-components"; const slideIn = keyframes` from { opacity: 0; } to { opacity: 1; } `; const Toast = styled.div` animation: ${slideIn} 0.5s cubic-bezier(0.4, 0, 0.2, 1) both; border-radius: 5px; padding: 20px; position: fixed; `;

Stile globale

Sebbene l'obiettivo originale di CSS-in-JS e, per estensione, dei componenti con stile sia l'ambito degli stili, possiamo anche sfruttare lo stile globale dei componenti con stile. Poiché lavoriamo principalmente con stili con ambito, potresti pensare che sia un'impostazione di fabbrica invariabile, ma ti sbaglieresti. Pensaci: cos'è davvero lo scope? È tecnicamente possibile per noi, in nome dello stile globale, fare qualcosa di simile a questo:

 ReactDOM.render( <StyledApp> <App /> </StyledApp>, document.getElementById("root") );

Ma abbiamo già una funzione di supporto, createGlobalStyle , la cui unica ragione di esistenza è lo stile globale. Allora, perché negargli la sua responsabilità?

Una cosa per cui possiamo usare createGlobalStyle è normalizzare il CSS:

 import {createGlobalStyle} from "styled-components"; const GlobalStyle = createGlobalStyle` /* Your css reset here */ `; // Use your GlobalStyle function App() { return ( <div> <GlobalStyle /> <Routes /> </div> ); }

Nota: gli stili creati con createGlobalStyle non accettano figli. Scopri di più nella documentazione.

A questo punto, ti starai chiedendo perché dovremmo preoccuparci di usare createGlobalStlye . Ecco alcuni motivi:

  • Non possiamo indirizzare nulla al di fuori del rendering radice senza di esso (ad esempio, html , body , ecc.).
  • createGlobalStyle inserisce gli stili ma non esegue il rendering di alcun elemento effettivo. Se osservi attentamente l'ultimo esempio, noterai che non abbiamo specificato alcun elemento HTML da visualizzare. Questo è bello perché potremmo non aver effettivamente bisogno dell'elemento. Dopotutto, ci occupiamo di stili globali. Ci rivolgiamo a selettori ampi, non a elementi specifici.
  • createGlobalStyle non ha l'ambito e può essere visualizzato ovunque nella nostra app e sarà applicabile fintanto che si trova nel DOM. Pensa al concetto , non alla struttura .
 import {createGlobalStyle} from "styled-components"; const GlobalStyle = createGlobalStyle` /* Your css reset here */ .app-title { font-size: 40px; } `; const StyledNav = styled.nav` /* Your styles here */ `; function Nav({children}) { return ( <StyledNav> <GlobalStyle /> {children} </StyledNav> ); } function App() { return ( <div> <Nav> <h1 className="app-title">STYLED COMPONENTS</h1> </Nav> <Main /> <Footer /> </div> ); }

Se pensi alla struttura, app-title non dovrebbe avere lo stile impostato in GlobalStyle . Ma non funziona in questo modo. Ovunque tu scelga di renderizzare il tuo GlobalStyle , verrà iniettato quando il tuo componente viene renderizzato .

Fai attenzione : createGlobalStyles verrà renderizzato solo se e quando è nel DOM.

Aiuto CSS

Abbiamo già visto come adattare gli stili in base agli oggetti di scena. E se volessimo andare un po' più in là? La funzione di supporto CSS aiuta a raggiungere questo obiettivo. Supponiamo di avere due campi di input di testo con stati: vuoto e attivo, ciascuno con un colore diverso. Possiamo farcela:

 const StyledTextField = styled.input` color: ${(props) => (props.isEmpty ? "none" : "black")}; `;

Va tutto bene. Successivamente, se dobbiamo aggiungere un altro stato di riempimento, dovremo modificare i nostri stili:

 const StyledTextField = styled.input` color: ${(props) => props.isEmpty ? "none" : props.active ? "purple" : "blue"}; `;

Ora l'operazione ternaria sta crescendo in complessità. Cosa succede se in seguito aggiungiamo un altro stato ai nostri campi di input di testo? O se volessimo dare a ogni stato stili aggiuntivi, oltre al colore? Riesci a immaginare di restringere gli stili nell'operazione ternaria? L'helper css è utile.

 const StyledTextField = styled.input` width: 100%; height: 40px; ${(props) => (props.empty && css` color: none; backgroundcolor: white; `) || (props.active && css` color: black; backgroundcolor: whitesmoke; `)} `;

Quello che abbiamo fatto è stato in qualche modo ampliare la nostra sintassi ternaria per accogliere più stili e con una sintassi più comprensibile e organizzata. Se l'affermazione precedente sembra sbagliata, è perché il codice sta cercando di fare troppo. Quindi, facciamo un passo indietro e perfezioniamo:

 const StyledTextField = styled.input` width: 100%; height: 40px; // 1. Empty state ${(props) => props.empty && css` color: none; backgroundcolor: white; `} // 2. Active state ${(props) => props.active && css` color: black; backgroundcolor: whitesmoke; `} // 3. Filled state ${(props) => props.filled && css` color: black; backgroundcolor: white; border: 1px solid green; `} `;

La nostra raffinatezza divide lo stile in tre diversi blocchi gestibili e di facile comprensione. È una vittoria.

Gestore foglio di stile

Come l'helper CSS, StyleSheetManager è un metodo di supporto per modificare il modo in cui gli stili vengono elaborati. Occorrono alcuni oggetti di scena, come disableVendorPrefixes (puoi controllare l'elenco completo), che ti aiutano a disattivare i prefissi dei fornitori dal suo sottoalbero.

 import styled, {StyleSheetManager} from "styled-components"; const StyledCard = styled.div` width: 200px; backgroundcolor: white; `; const StyledNav = styled.div` width: calc(100% - var(--side-nav-width)); `; function Profile() { return ( <div> <StyledNav /> <StyleSheetManager disableVendorPrefixes> <StyledCard> This is a card </StyledCard> </StyleSheetManager> </div> ); }

disableVendorPrefixes viene passato come prop a <StyleSheetManager> . Quindi, i componenti con stile racchiusi da <StyleSheetManager> verrebbero disabilitati, ma non quelli in <StyledNav> .

Debug più semplice

Quando ho introdotto componenti in stile a uno dei miei colleghi, una delle loro lamentele è stata che è difficile individuare un elemento renderizzato nel DOM - o in React Developer Tools, se è per questo. Questo è uno degli svantaggi dei componenti con stile: nel tentativo di fornire nomi di classe univoci, assegna hash univoci agli elementi, che sono criptici, ma rende leggibile displayName per facilitare il debug.

 import React from "react"; import styled from "styled-components"; import "./App.css"; const LoginButton = styled.button` background-color: white; color: black; border: 1px solid red; `; function App() { return ( <div className="App"> <LoginButton>Login</LoginButton> </div> ); }

Per impostazione predefinita, i componenti con stile rendono LoginButton come <button class="LoginButton-xxxx xxxx">Login</button> nel DOM e come LoginButton in React Developer Tools, il che semplifica il debug. Possiamo attivare il displayName booleano se non vogliamo questo comportamento. Ciò richiede una configurazione Babel.

Nota : nella documentazione è specificato il pacchetto babel-plugin-styled-components , così come un file di configurazione .babelrc . Il problema con questo è che, poiché stiamo usando create-react-app , non possiamo configurare molte cose a meno che non espelliamo. È qui che entrano in gioco le macro di Babel.

Dovremo installare babel-plugin-macros con npm o Yarn, quindi creare un babel-plugin-macros.config.js nella radice della nostra applicazione, con il contenuto:

 module.exports = { styledComponents: { displayName: true, fileName: false, }, };

Con il valore fileName invertito, displayName sarà preceduto dal nome del file per una precisione ancora più unica.

Ora dobbiamo anche importare dalla macro :

 // Before import styled from "styled-components"; // After import styled from "styled-components/macro";

Conclusione

Ora che puoi comporre il tuo CSS in modo programmatico, non abusare della libertà. Per quel che vale, fai del tuo meglio per mantenere la sanità mentale nei tuoi componenti in stile. Non cercare di comporre condizionali pesanti, né supporre che ogni cosa debba essere un componente con uno stile. Inoltre, non esagerare creando componenti in stile nascente per casi d'uso che stai solo indovinando sono da qualche parte dietro l'angolo.

Ulteriori risorse

  1. Documentazione, componenti stilizzati
  2. "Costruzione di un sistema di componenti riutilizzabili con React.js e componenti in stile", Lukas Gisder-Dube
  3. Utilizzo con Next.js
  4. Utilizzo con Gatsby