Construirea de componente React reutilizabile folosind Tailwind
Publicat: 2022-03-10În această postare, vom analiza mai multe moduri diferite în care puteți construi componente React reutilizabile care folosesc Tailwind sub capotă în timp ce expuneți o interfață plăcută altor componente. Acest lucru vă va îmbunătăți codul, trecând de la liste lungi de nume de clase la elemente semantice care sunt mai ușor de citit și de întreținut.
Va trebui să fi lucrat cu React pentru a înțelege bine această postare.
Tailwind este un cadru CSS foarte popular care oferă clase de utilitate de nivel scăzut pentru a ajuta dezvoltatorii să construiască design-uri personalizate. A crescut în popularitate în ultimii ani, deoarece rezolvă foarte bine două probleme:
- Tailwind facilitează efectuarea de modificări iterative la HTML fără a căuta prin foile de stil pentru a găsi selectoare CSS potrivite.
- Tailwind are convenții și valori implicite sănătoase. Acest lucru face ca oamenii să înceapă ușor fără a scrie CSS de la zero.
Adăugați documentația cuprinzătoare și nu este surprinzător de ce Tailwind este atât de popular.
Aceste metode vă vor ajuta să transformați codul care arată astfel:
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> Enable </button>
Pentru a codifica care arată astfel:
<Button size="sm" textColor="white" bgColor="blue-500"> Enable </Button>
Diferența dintre ambele fragmente este că în primul am folosit o etichetă standard de buton HTML, în timp ce al doilea a folosit o componentă <Button>
. Componenta <Button>
a fost construită pentru a fi reutilizată și este mai ușor de citit, deoarece are o semantică mai bună. În loc de o listă lungă de nume de clase, folosește proprietăți pentru a seta diverse atribute, cum ar fi size
, textColor
și bgColor
.
Să începem.
Metoda 1: Controlul claselor cu modulul Classnames
O modalitate simplă de a adapta Tailwind într-o aplicație React este să îmbrățișați numele claselor și să le comutați programatic.
Modulul classnames npm facilitează comutarea între clase în React. Pentru a demonstra cum puteți utiliza acest lucru, să luăm un caz de utilizare în care aveți componente <Button>
în aplicația dvs. React.
// This could be hard to read. <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Enable</button> // This is more conventional React. <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>
Să vedem cum să separăm clasele Tailwind, astfel încât oamenii care folosesc această componentă <Button>
să poată folosi elemente de recuzită React, cum ar fi size
, textColor
și bgColor
.
- Treceți elemente de recuzită precum
bgColor
șitextColor
direct în șablonul șir de nume de clasă. - Utilizați obiecte pentru a schimba în mod programatic numele claselor (cum am făcut cu
size
prop)
În exemplul de cod de mai jos, vom arunca o privire asupra ambelor abordări.
// Button.jsx import classnames from 'classnames'; function Button ({size, bgColor, textColor, children}) { return ( <button className={classnames("bg-${bgColor} text-${textColor} font-bold py-2 px-4 rounded", { "text-xs": size === 'sm' "text-xl": size === 'lg', })}> {children} </button> ) }; export default Button;
În codul de mai sus, definim o componentă Button
care are următoarele elemente de recuzită:
-
size
Definește dimensiunea butonului și aplică clasele Tailwindtext-xs
sautext-xl
-
bgColor
Definește culoarea de fundal a butonului și aplică clasele Tailwindbg-*
. -
textColor
Definește culoarea textului butonului și aplicătext-* classes
. -
children
Orice subcomponente vor fi trecute aici. De obicei, va conține textul din<Button>
.
Prin definirea Button.jsx
, acum îl putem importa și folosi elemente de recuzită React în loc de numele claselor. Acest lucru face codul nostru mai ușor de citit și reutilizat.
import Button from './Button'; <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>
Utilizarea numelor de clasă pentru componentele interactive
Un buton este un caz de utilizare foarte simplu. Ce zici de ceva mai complicat? Ei bine, puteți duce acest lucru mai departe pentru a crea componente interactive.
De exemplu, să ne uităm la un meniu derulant care este realizat folosind Tailwind.
Un meniu derulant interactiv creat folosind Tailwind și comutarea numelui clasei.
Pentru acest exemplu, creăm componenta HTML folosind nume de clasă CSS Tailwind, dar expunem o componentă React care arată astfel:
<Dropdown options={\["Edit", "Duplicate", "Archive", "Move", "Delete"\]} onOptionSelect={(option) => { console.log("Selected Option", option)} } />
Privind codul de mai sus, veți observa că nu avem nicio clasă Tailwind. Toate sunt ascunse în codul de implementare al <Dropdown/>
. Utilizatorul acestei componente Dropdown
trebuie doar să furnizeze o listă de options
și un handler de clic, pe onOptionSelect
atunci când se face clic pe o option
.
Să vedem cum poate fi construită această componentă folosind Tailwind.
Îndepărtând o parte din codul care nu are legătură, iată cheia logicii. Puteți vizualiza acest Codepen pentru un exemplu complet.
import classNames from 'classnames'; function Dropdown({ options, onOptionSelect }) { // Keep track of whether the dropdown is open or not. const [isActive, setActive] = useState(false); const buttonClasses = `inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-blue-500 active:text-gray-200 transition ease-in-out duration-150`; return ( // Toggle the dropdown if the button is clicked <button onClick={() => setActive(!isActive)} className={buttonClasses}> Options </button> // Use the classnames module to toggle the Tailwind .block and .hidden classes <div class={classNames("origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg", { block: isActive, hidden: !isActive })}> // List items are rendered here. {options.map((option) => <div key={option} onClick={(e) => onOptionSelect(option)}>{option}</div>)} </div> ) } export default Dropdown;
Meniul derulant este făcut interactiv prin afișarea sau ascunderea selectivă folosind clasele .hidden
și .block
. Ori de câte ori <button>
este apăsat, declanșăm handlerul onClick
care comută starea isActive
. Dacă butonul este activ ( isActive === true
), setăm clasa de block
. În caz contrar, setăm clasa hidden
. Acestea sunt ambele clase Tailwind pentru comutarea comportamentului de afișare.
În rezumat, modulul classnames este o modalitate simplă și eficientă de a controla programatic numele claselor pentru Tailwind. Facilitează separarea logicii în elementele de recuzită React, ceea ce face componentele mai ușor de reutilizat. Funcționează pentru componente simple și interactive.
Metoda 2: Utilizarea constantelor pentru a defini un sistem de proiectare
Un alt mod de a folosi împreună Tailwind și React este prin utilizarea constantelor și maparea elementelor de recuzită la o constantă specifică. Acest lucru este eficient pentru sistemele de proiectare a clădirilor. Să demonstrăm cu un exemplu.
Începeți cu un fișier theme.js
în care vă enumerați sistemul de design.
// theme.js (you can call it whatever you want) export const ButtonType = { primary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", secondary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", basic: "bg-white hover:bg-gray-700 text-gray-700 font-bold rounded", delete: "bg-red-300 hover:bg-red-500 text-white font-bold rounded" }; export const ButtonSize = { sm: "py-2 px-4 text-xs", lg: "py-3 px-6 text-lg" }
În acest caz, avem două seturi de constante:
-
ButtonType
definește modul în care butoanele sunt stilate în aplicația noastră. -
ButtonSizes
definește dimensiunile butoanelor din aplicația noastră.
Acum, să scriem componenta noastră <Button>
:
import {ButtonType, ButtonSize} from './theme'; function Button({size, type, children}) { // This can be improved. I'm keeping it simple here by joining two strings. const classNames = ButtonType[type] + " " + ButtonSize[size]; return ( <button className={classNames}>{children}</button> ) } export default Button;
Folosim constantele ButtonType
și ButtonSize
pentru a crea o listă de nume de clase. Acest lucru face interfața <Button>
mult mai plăcută. Ne permite să folosim elemente de recuzită pentru size
și type
în loc să punem totul într-un șir de nume de clasă.
// Cleaner and well defined props. <Button size="xs" type="primary">Enable</Button>
Față de abordarea anterioară:
// Exposing class names <button className="py-2 px-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Enable</button>
Dacă trebuie să redefiniți cum arată butoanele în aplicația dvs., trebuie doar să editați fișierul theme.js
și toate butoanele din aplicația dvs. se vor actualiza automat. Acest lucru poate fi mai ușor decât căutarea numelor de clasă în diferite componente.
Metoda 3: Compunerea utilităților cu @apply
O a treia modalitate de a îmbunătăți lizibilitatea componentelor dvs. React este utilizarea CSS și a modelului @apply
disponibil în PostCSS pentru a extrage clase repetate. Acest model implică utilizarea foilor de stil și post-procesoarelor.
Să demonstrăm cum funcționează acest lucru printr-un exemplu. Să presupunem că aveți un grup de butoane care are un buton primar și un buton secundar.
<button className="py-2 px-4 mr-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Update Now</button> <button className="py-2 px-4 text-xs mr-4 hover:bg-gray-100 text-gray-700 border-gray-300 border font-bold rounded">Later</button>
Folosind modelul @apply
, puteți scrie acest HTML ca:
<button className="btn btn-primary btn-xs">Update Now</button> <button className="btn btn-secondary btn-xs">Later</button>
Care poate fi apoi adoptat pentru React pentru a deveni:
import classnames from "classnames"; function Button ({size, type, children}) { const bSize = "btn-" + size; const bType = "btn-" + type; return ( <button className={classnames("btn", bSize, bType)}>{children}</button> ) } Button.propTypes = { size: PropTypes.oneOf(['xs, xl']), type: PropTypes.oneOf(['primary', 'secondary']) }; // Using the Button component. <Button type="primary" size="xs">Update Now</Button> <Button type="secondary" size="xs">Later</Button>
Iată cum ați crea aceste nume de clasă în stil BEM, cum ar fi .btn
, .btn-primary
și altele. Începeți prin a crea un fișier button.css
:
/\* button.css \*/ @tailwind base; @tailwind components; .btn { @apply py-2 px-4 mr-4 font-bold rounded; } .btn-primary { @apply bg-blue-500 hover:bg-blue-700 text-white; } .btn-secondary { @apply hover:bg-gray-700 text-gray-700 border-gray-300 border; } .btn-xs { @apply text-xs; } .btn-xl { @apply text-xl; } @tailwind utilities;
Codul de mai sus nu este CSS real , dar va fi compilat de PostCSS. Există un depozit GitHub disponibil aici, care arată cum să configurați PostCSS și Tailwind pentru un proiect JavaScript.
Există, de asemenea, un scurt videoclip care demonstrează cum să-l configurați aici.
Dezavantajele @apply
Conceptul de extragere a claselor de utilitate Tailwind în clase CSS de nivel superior pare să aibă sens, dar are câteva dezavantaje de care ar trebui să fii conștient. Să le evidențiem cu un alt exemplu.
În primul rând, prin extragerea acestor nume de clasă, pierdem unele informații. De exemplu, trebuie să fim conștienți de faptul că .btn-primary
trebuie adăugat la o componentă care are deja aplicat .btn
. De asemenea, .btn-primary
și .btn-secondary
nu pot fi aplicate împreună. Această informație nu este evidentă doar privind la clase.
Dacă această componentă ar fi ceva mai complicat, ar trebui să înțelegeți și relația părinte-copil dintre clase. Într-un fel, aceasta este problema pentru care Tailwind a fost proiectat să o rezolve și, folosind @apply
, readucem problemele, într-un mod diferit.
Iată un videoclip în care Adam Wathan – creatorul lui Tailwind – se scufundă în avantajele și dezavantajele utilizării @apply
.
rezumat
În acest articol, am analizat trei moduri prin care puteți integra Tailwind într-o aplicație React pentru a construi componente reutilizabile. Aceste metode vă ajută să construiți componente React care au o interfață mai curată folosind elemente de props
.
- Utilizați modulul Classnames pentru a comuta programatic între clase.
- Definiți un fișier de constante în care definiți o listă de clase per starea componentei.
- Utilizați
@apply
pentru a extrage clase CSS de nivel superior.
Dacă aveți întrebări, trimiteți-mi un mesaj pe Twitter la @tilomitra.
Lectură recomandată pe SmashingMag:
- Configurarea CSS Tailwind într-un proiect React
- Crearea de tabele sortabile cu React
- Un ghid pentru instrumentele CSS DevTools noi și experimentale în Firefox
- Creați propriile panouri de conținut pentru extindere și contractare