Tworzenie komponentów React wielokrotnego użytku za pomocą Tailwind
Opublikowany: 2022-03-10W tym poście przyjrzymy się kilku różnym sposobom budowania komponentów Reacta wielokrotnego użytku, które wykorzystują Tailwind pod maską, jednocześnie eksponując przyjemny interfejs innym komponentom. Poprawi to Twój kod, przechodząc od długich list nazw klas do właściwości semantycznych, które są łatwiejsze do odczytania i utrzymania.
Aby dobrze zrozumieć ten post, musisz współpracować z Reactem.
Tailwind to bardzo popularna platforma CSS, która zapewnia niskopoziomowe klasy narzędziowe, które pomagają programistom tworzyć niestandardowe projekty. W ciągu ostatnich kilku lat zyskał na popularności, ponieważ naprawdę dobrze rozwiązuje dwa problemy:
- Tailwind ułatwia wprowadzanie iteracyjnych zmian w HTML bez przeszukiwania arkuszy stylów w celu znalezienia pasujących selektorów CSS.
- Tailwind ma rozsądne konwencje i wartości domyślne. Ułatwia to ludziom rozpoczęcie pracy bez pisania CSS od podstaw.
Dodaj obszerną dokumentację i nie dziwi, dlaczego Tailwind jest tak popularny.
Te metody pomogą Ci przekształcić kod, który wygląda tak:
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> Enable </button>
Aby kod wyglądał tak:
<Button size="sm" textColor="white" bgColor="blue-500"> Enable </Button>
Różnica między obydwoma fragmentami polega na tym, że w pierwszym wykorzystaliśmy standardowy znacznik HTML button, podczas gdy w drugim użyliśmy komponentu <Button>
. Składnik <Button>
został zbudowany z myślą o ponownym użyciu i jest łatwiejszy do odczytania, ponieważ ma lepszą semantykę. Zamiast długiej listy nazw klas używa właściwości do ustawiania różnych atrybutów, takich jak size
, textColor
i bgColor
.
Zacznijmy.
Metoda 1: Kontrolowanie klas za pomocą modułu nazw klas
Prostym sposobem dostosowania Tailwind do aplikacji React jest objęcie nazw klas i programowe ich przełączanie.
Moduł classnames npm ułatwia przełączanie klas w React. Aby zademonstrować, jak możesz tego użyć, weźmy przypadek użycia, w którym masz komponenty <Button>
w swojej aplikacji 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>
Zobaczmy, jak oddzielić klasy Tailwind, aby ludzie używający tego komponentu <Button>
mogli używać właściwości React, takich jak size
, textColor
i bgColor
.
- Przekaż właściwości, takie jak
bgColor
itextColor
bezpośrednio do szablonu ciągu nazwy klasy. - Użyj obiektów do programowego przełączania nazw klas (tak jak zrobiliśmy to z propozycją
size
)
W poniższym przykładowym kodzie przyjrzymy się obu podejściom.
// 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;
W powyższym kodzie definiujemy komponent Button
, który przyjmuje następujące właściwości:
-
size
Definiuje rozmiar przycisku i stosuje klasy Tailwindtext-xs
lubtext-xl
-
bgColor
Definiuje kolor tła przycisku i stosuje klasy Tailwindbg-*
. -
textColor
Definiuje kolor tekstu przycisku i stosujetext-* classes
. -
children
Wszystkie podkomponenty zostaną tutaj przekazane. Zwykle zawiera tekst w<Button>
.
Definiując Button.jsx
, możemy go teraz zaimportować i używać właściwości React zamiast nazw klas. Dzięki temu nasz kod jest łatwiejszy do odczytania i ponownego użycia.
import Button from './Button'; <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>
Używanie nazw klas dla komponentów interaktywnych
Przycisk jest bardzo prostym przypadkiem użycia. A co z czymś bardziej skomplikowanym? Cóż, możesz posunąć się dalej, aby tworzyć interaktywne komponenty.
Spójrzmy na przykład na listę rozwijaną utworzoną za pomocą Tailwind.
Interaktywne menu rozwijane zbudowane przy użyciu Tailwind i przełączania nazw klas.
W tym przykładzie tworzymy komponent HTML przy użyciu nazw klas CSS Tailwind, ale udostępniamy komponent React, który wygląda tak:
<Dropdown options={\["Edit", "Duplicate", "Archive", "Move", "Delete"\]} onOptionSelect={(option) => { console.log("Selected Option", option)} } />
Patrząc na powyższy kod, zauważysz, że nie mamy żadnych klas Tailwind. Wszystkie są ukryte w kodzie implementacji <Dropdown/>
. Użytkownik tego komponentu Dropdown
musi tylko dostarczyć listę options
i procedurę obsługi kliknięcia, onOptionSelect
po kliknięciu option
.
Zobaczmy, jak można zbudować ten komponent za pomocą Tailwind.
Usunięcie części niepowiązanego kodu, oto sedno logiki. Możesz zobaczyć ten Codepen dla pełnego przykładu.
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;
Menu rozwijane jest interaktywne poprzez selektywne pokazywanie lub ukrywanie go za pomocą klas .hidden
i .block
. Za każdym razem, gdy naciśnięty jest <button>
, uruchamiamy procedurę obsługi onClick
, która przełącza stan isActive
. Jeżeli przycisk jest aktywny ( isActive === true
), ustawiamy klasę block
. W przeciwnym razie ustawiamy hidden
klasę. Są to obie klasy Tailwind do przełączania zachowania wyświetlania.

Podsumowując, moduł nazw klas to prosty i skuteczny sposób na programowe kontrolowanie nazw klas dla Tailwind. Ułatwia rozdzielenie logiki na właściwości React, co ułatwia ponowne wykorzystanie Twoich komponentów. Działa dla prostych i interaktywnych komponentów.
Metoda 2: Używanie stałych do zdefiniowania systemu projektowego
Innym sposobem używania Tailwind i React razem jest użycie stałych i mapowanie właściwości na określoną stałą. Jest to skuteczne w przypadku systemów projektowania budynków. Zademonstrujmy na przykładzie.
Zacznij od pliku theme.js
, w którym wymieniasz swój system projektowania.
// 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" }
W tym przypadku mamy dwa zestawy stałych:
-
ButtonType
definiuje sposób stylizacji przycisków w naszej aplikacji. -
ButtonSizes
określa rozmiary przycisków w naszej aplikacji.
Teraz napiszmy nasz komponent <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;
Używamy stałych ButtonType
i ButtonSize
do tworzenia listy nazw klas. Dzięki temu interfejs naszego <Button>
jest znacznie ładniejszy. Pozwala nam używać właściwości size
i type
zamiast umieszczać wszystko w ciągu nazwy klasy.
// Cleaner and well defined props. <Button size="xs" type="primary">Enable</Button>
W przeciwieństwie do wcześniejszego podejścia:
// 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>
Jeśli chcesz zmienić wygląd przycisków w swojej aplikacji, po prostu edytuj plik theme.js
, a wszystkie przyciski w aplikacji zostaną automatycznie zaktualizowane. Może to być łatwiejsze niż wyszukiwanie nazw klas w różnych komponentach.
Metoda 3: komponowanie narzędzi za pomocą @apply
Trzecim sposobem na poprawę czytelności komponentów React jest użycie CSS i wzorca @apply
dostępnego w PostCSS w celu wyodrębnienia powtarzających się klas. Ten wzorzec obejmuje użycie arkuszy stylów i postprocesorów.
Pokażmy, jak to działa na przykładzie. Załóżmy, że masz grupę Button, która ma przycisk podstawowy i dodatkowy.

<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>
Używając wzorca @apply
, możesz napisać ten kod HTML jako:
<button className="btn btn-primary btn-xs">Update Now</button> <button className="btn btn-secondary btn-xs">Later</button>
Które następnie można zaadaptować do React, aby stać się:
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>
Oto, jak utworzyć te nazwy klas w stylu BEM, takie jak .btn
, .btn-primary
i inne. Zacznij od utworzenia pliku 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;
Powyższy kod nie jest prawdziwym CSS, ale zostanie skompilowany przez PostCSS. Dostępne jest tutaj repozytorium GitHub, które pokazuje, jak skonfigurować PostCSS i Tailwind dla projektu JavaScript.
Jest też krótki film, który pokazuje, jak to skonfigurować.
Wady korzystania z @apply
Koncepcja wyodrębniania klas narzędziowych Tailwind do klas CSS wyższego poziomu wydaje się mieć sens, ale ma pewne wady, o których należy pamiętać. Podkreślmy je innym przykładem.
Po pierwsze, wyodrębniając te nazwy klas, tracimy część informacji. Na przykład musimy mieć świadomość, że .btn-primary
musi zostać dodany do komponentu, do którego już zastosowano .btn
. Ponadto .btn-primary
i .btn-secondary
nie mogą być stosowane razem. Ta informacja nie jest oczywista po prostu patrząc na zajęcia.
Gdyby ten składnik był czymś bardziej skomplikowanym, musiałbyś również zrozumieć relację rodzic-dziecko między klasami. W pewnym sensie jest to problem, który ma rozwiązać Tailwind. Używając @apply
, przywracamy problemy w inny sposób.
Oto film, w którym Adam Wathan — twórca Tailwind — omawia zalety i wady korzystania z @apply
.
Streszczenie
W tym artykule przyjrzeliśmy się trzem sposobom integracji Tailwind z aplikacją React w celu tworzenia komponentów wielokrotnego użytku. Te metody pomagają budować komponenty Reacta, które mają czystszy interfejs przy użyciu props
.
- Użyj modułu nazw klas, aby programowo przełączać klasy.
- Zdefiniuj plik stałych, w którym zdefiniujesz listę klas według stanu komponentu.
- Użyj
@apply
, aby wyodrębnić klasy CSS wyższego poziomu.
Jeśli masz jakieś pytania, wyślij mi wiadomość na Twitterze pod adresem @tilomitra.
Zalecana lektura na SmashingMag:
- Konfigurowanie CSS Tailwind w projekcie React
- Tworzenie tabel, które można sortować w React
- Przewodnik po nowych i eksperymentalnych narzędziach CSS DevTools w Firefoksie
- Twórz własne rozszerzające się i kurczące panele treści