Cum să utilizați componentele stilizate în React

Publicat: 2022-03-10
Rezumat rapid ↬ În timp ce abordarea bazată pe componente a introdus o nouă frontieră în modul în care construim aplicații web, aceasta nu este lipsită de imperfecțiunile sale - una fiind utilizarea și scalabilitatea cu CSS. Acest lucru a dat naștere unui nou mod de a construi și gestiona stilurile noastre într-o manieră specifică componentelor , cunoscută altfel ca CSS-in-JS.

Componentele cu stil sunt un instrument CSS-in-JS care face o punte între componente și stil, oferind numeroase funcții pentru a vă pune în funcțiune componentele de stil într-un mod funcțional și reutilizabil. În acest articol, veți învăța elementele de bază ale componentelor stilate și cum să le aplicați corect aplicațiilor dvs. React. Ar fi trebuit să lucrați la React înainte de a parcurge acest tutorial. Dacă sunteți în căutarea diferitelor opțiuni de aranjare a componentelor React, puteți consulta postarea noastră anterioară pe acest subiect.

La baza CSS se află capacitatea de a viza orice element HTML - la nivel global - indiferent de poziția acestuia în arborele DOM. Acest lucru poate fi o piedică atunci când sunt utilizate împreună cu componente, deoarece componentele necesită, într-o măsură rezonabilă, o colocare (adică păstrarea activelor precum state și stil) mai aproape de locul în care sunt utilizate (cunoscută sub numele de localizare).

În propriile cuvinte ale lui React, componentele stilizate sunt „ primitive vizuale pentru componente ”, iar scopul lor este să ne ofere o modalitate flexibilă de stilare a componentelor. Rezultatul este o cuplare strânsă între componente și stilurile acestora.

Notă: Componentele cu stil sunt disponibile atât pentru React, cât și pentru React Native și, deși cu siguranță ar trebui să consultați ghidul React Native, accentul nostru aici se va concentra pe componentele cu stil pentru React.

Mai multe după săritură! Continuați să citiți mai jos ↓

De ce Componente stilate?

Pe lângă faptul că vă ajută să definiți stilurile, componentele stilate includ următoarele caracteristici:

  • Prefixarea automată a furnizorului
    Puteți utiliza proprietăți CSS standard, iar componentele cu stil vor adăuga prefixe de furnizor dacă sunt necesare.
  • Nume unice de clasă
    Componentele stilate sunt independente unele de altele și nu trebuie să vă faceți griji cu privire la numele lor, deoarece biblioteca se ocupă de asta pentru dvs.
  • Eliminarea stilurilor moarte
    Componentele stilate elimină stilurile neutilizate, chiar dacă sunt declarate în codul dvs.
  • si multe altele.

Instalare

Instalarea componentelor stilate este ușoară. Puteți face acest lucru printr-un CDN sau cu un manager de pachete, cum ar fi Yarn...

 yarn add styled-components

… sau npm:

 npm i styled-components

Demo-ul nostru folosește aplicația create-react.

Început

Poate că primul lucru pe care îl veți observa despre componentele stilate este sintaxa lor, care poate fi descurajantă dacă nu înțelegeți magia din spatele componentelor stilate. Pentru a spune pe scurt, componentele stilizate folosesc literalele șablonului JavaScript pentru a reduce decalajul dintre componente și stiluri. Deci, atunci când creați o componentă cu stil, ceea ce creați de fapt este o componentă React cu stiluri. Arata cam asa:

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

Aici, StyledButton este componenta stilată și va fi redată ca un buton HTML cu stilurile conținute. styled este o metodă de utilitate internă care transformă stilul din JavaScript în CSS real.

În HTML brut și CSS, am avea asta:

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

Dacă componentele stilizate sunt componente React, putem folosi elemente de recuzită? Da putem.

Adaptare pe baza de recuzită

Componentele stilizate sunt funcționale , așa că putem stila cu ușurință elementele în mod dinamic. Să presupunem că avem două tipuri de butoane pe pagina noastră, unul cu fundal negru, iar celălalt albastru. Nu trebuie să creăm două componente stilizate pentru ele; le putem adapta stilul în funcție de recuzită.

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

Deoarece StyledButton este o componentă React care acceptă elemente de recuzită, putem aloca o culoare de fundal diferită în funcție de existența sau valoarea prop-ului bg .

Veți observa, totuși, că nu am dat butonul nostru un type . Hai să facem asta:

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

Componentele stilizate pot diferenția între tipurile de recuzită pe care le primesc. Ei știu că type este un atribut HTML, așa că redă de fapt <button type="button">Button A</button> , în timp ce folosesc prop bg în propria procesare. Observați cum am atașat și un handler de evenimente?

Vorbind de atribute, o sintaxă extinsă ne permite să gestionăm elementele de recuzită folosind constructorul attrs . Verificați asta:

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

Observați că nu avem nevoie de un ternar când setăm lățimea? Asta pentru că am setat deja o valoare implicită pentru el cu width: props.width || "100%", width: props.width || "100%", . De asemenea, am folosit proprietăți personalizate CSS pentru că putem!

Notă: Dacă componentele stilate sunt componente React și putem trece elemente de recuzită, atunci putem folosi și stări? Contul GitHub al bibliotecii are o problemă care abordează această problemă.

Stiluri de extindere

Să presupunem că lucrați la o pagină de destinație și ați setat containerul la o anumită lățime maximă pentru a menține lucrurile centrate. Ai un StyledContainer pentru asta:

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

Apoi, descoperi că ai nevoie de un container mai mic, cu umplutură de 10 pixeli pe ambele părți, în loc de 20 de pixeli. Primul tău gând ar putea fi să creezi o altă componentă stilată și ai avea dreptate, dar nu va dura timp până să realizezi că duplicați stiluri.

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

Înainte de a continua și de a crea StyledSmallContainer , ca în fragmentul de mai sus, să învățăm modul de a reutiliza și de a moșteni stilurile. Este mai mult sau mai puțin ca și cum funcționează operatorul de 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> ); }

În StyledSmallContainer , veți obține toate stilurile din StyledContainer , dar umplutura va fi suprascrisă. Rețineți că, în mod obișnuit, veți obține un element de secțiune randat pentru StyledSmallContainer , deoarece asta este ceea ce redă StyledContainer . Dar asta nu înseamnă că este sculptat în piatră sau neschimbat.

Prop. polimorfic „ca”.

Cu suportul as polimorf, puteți schimba elementul final care este redat. Un caz de utilizare este atunci când moșteniți stiluri (ca în ultimul exemplu). Dacă, de exemplu, preferați un div într-o section pentru StyledSmallContainer , puteți transmite as prop componenta stilată cu valoarea elementului preferat, astfel:

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

Acum, StyledSmallContainer va fi redat ca div . Puteți avea chiar și o componentă personalizată ca valoare:

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

Nu o lua de la sine înțeles.

Sintaxă asemănătoare SCSS

Preprocesorul CSS Stylis permite componentelor cu stil să accepte sintaxă asemănătoare SCSS, cum ar fi imbricarea:

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

Animaţie

Componentele stilate au un ajutor pentru keyframes care ajută la construirea cadrelor cheie de animație (reutilizabile). Avantajul aici este că cadrele cheie vor fi detașate de componentele stilate și pot fi exportate și reutilizate oriunde este nevoie.

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

Stil global

În timp ce obiectivul inițial al CSS-in-JS și, prin extensie, al componentelor stilate este definirea stilurilor, putem, de asemenea, să folosim stilul global al componentelor stilate. Deoarece lucrăm în mare parte cu stiluri definite, ați putea crede că este o setare invariabilă din fabrică, dar ați greși. Gândiți-vă la asta: ce este de fapt domeniul de aplicare? Este posibil din punct de vedere tehnic pentru noi – în numele stilului global – să facem ceva similar cu acesta:

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

Dar avem deja o funcție de ajutor – createGlobalStyle – al cărei singur motiv de existență este stilul global. Deci, de ce să-i negați responsabilitatea?

Un lucru pentru care putem folosi createGlobalStyle este pentru a normaliza CSS:

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

Notă: Stilurile create cu createGlobalStyle nu acceptă copii. Aflați mai multe în documentație.

În acest moment, s-ar putea să vă întrebați de ce ar trebui să ne deranjam să folosim createGlobalStlye . Iată câteva motive:

  • Nu putem viza nimic în afara redării rădăcină fără acesta (de exemplu, html , body , etc.).
  • createGlobalStyle injectează stiluri, dar nu redă niciun element real. Dacă te uiți la ultimul exemplu îndeaproape, vei observa că nu am specificat niciun element HTML de randat. Este grozav pentru că s-ar putea să nu avem nevoie de element. La urma urmei, ne preocupă stilurile globale. Vrim selectatori în general, nu elemente specifice.
  • createGlobalStyle nu este acoperit și poate fi redat oriunde în aplicația noastră și va fi aplicabil atâta timp cât se află în DOM. Gândiți-vă la concept , nu la structură .
 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> ); }

Dacă vă gândiți la structură, atunci app-title nu ar trebui să fie stilat așa cum este setat în GlobalStyle . Dar nu funcționează așa. Oriunde alegeți să GlobalStyle , acesta va fi injectat atunci când componenta dvs. este redată .

Fiți atenți : createGlobalStyles va fi randat numai dacă și când este în DOM.

Ajutor CSS

Am văzut deja cum să adaptăm stilurile bazate pe recuzită. Dacă am vrea să mergem puțin mai departe? Funcția de ajutor CSS ajută la realizarea acestui lucru. Să presupunem că avem două câmpuri de introducere a textului cu stări: gol și activ, fiecare cu o culoare diferită. Noi putem sa facem asta:

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

Totul e bine. Ulterior, dacă trebuie să adăugăm o altă stare de umplut, ar trebui să ne modificăm stilurile:

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

Acum operațiunea ternară crește în complexitate. Ce se întâmplă dacă mai târziu adăugăm o altă stare la câmpurile noastre de introducere a textului? Sau ce se întâmplă dacă vrem să oferim fiecărui stat stiluri suplimentare, altele decât culoarea? Îți poți imagina împingerea stilurilor în operația ternară? css este util.

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

Ceea ce am făcut a fost un fel de extins sintaxa noastră ternară pentru a se adapta mai multor stiluri și cu o sintaxă mai înțeleasă și organizată. Dacă afirmația anterioară pare greșită, este pentru că codul încearcă să facă prea mult. Deci, să facem un pas înapoi și să rafinăm:

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

Rafinamentul nostru împarte stilul în trei bucăți diferite, gestionabile și ușor de înțeles. Este o victorie.

StyleSheetManager

La fel ca ajutorul CSS, StyleSheetManager este o metodă de ajutor pentru modificarea modului în care sunt procesate stilurile. Este nevoie de anumite elemente de recuzită - cum ar fi disableVendorPrefixes (puteți verifica lista completă) - care vă ajută să renunțați la prefixele furnizorului din subarborele său.

 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 este transmis ca o prop la <StyleSheetManager> . Deci, componentele stilate înfășurate de <StyleSheetManager> ar fi dezactivate, dar nu și cele din <StyledNav> .

Depanare mai ușoară

Când am introdus componente cu stil unuia dintre colegii mei, una dintre plângerile lor a fost că este greu să găsiți un element randat în DOM - sau în React Developer Tools, de altfel. Acesta este unul dintre dezavantajele componentelor cu stil: în încercarea de a furniza nume de clasă unice, atribuie hashuri unice elementelor, care se întâmplă să fie criptice, dar face ca displayName să fie lizibil pentru o depanare mai ușoară.

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

În mod implicit, componentele cu stil redă LoginButton ca <button class="LoginButton-xxxx xxxx">Login</button> în DOM și ca LoginButton în React Developer Tools, ceea ce facilitează depanarea. Putem comuta displayName dacă nu dorim acest comportament. Acest lucru necesită o configurație Babel.

Notă : În documentație, este specificat pachetul babel-plugin-styled-components , precum și un fișier de configurare .babelrc . Problema cu aceasta este că, deoarece folosim create-react-app , nu putem configura multe lucruri decât dacă ejectăm. Aici intervin macrocomenzile Babel.

Va trebui să instalăm babel-plugin-macros cu npm sau Yarn, apoi să creăm un babel-plugin-macros.config.js la rădăcina aplicației noastre, cu conținutul:

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

Cu valoarea fileName inversată, displayName va fi prefixat cu numele fișierului pentru o precizie și mai unică.

De asemenea, acum trebuie să importăm din macro :

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

Concluzie

Acum că vă puteți compune CSS în mod programatic, nu abuzați de libertate. Pentru ceea ce merită, fă tot posibilul pentru a menține sănătatea mentală în componentele tale stilate. Nu încercați să compuneți condiționale grele și nici să presupuneți că fiecare lucru ar trebui să fie o componentă de stil. De asemenea, nu exagerați prin crearea de componente în stil în curs de dezvoltare pentru cazurile de utilizare pe care doar bănuiți că sunt undeva după colț.

Resurse suplimentare

  1. Documentație, Componente stilate
  2. „Construirea unui sistem de componente reutilizabile cu React.js și componente cu stil”, Lukas Gisder-Dube
  3. Utilizare cu Next.js
  4. Utilizare cu Gatsby