Jak używać Styled-Components w React
Opublikowany: 2022-03-10Styled components to narzędzie CSS-in-JS, które wypełnia lukę między komponentami a stylizacją, oferując liczne funkcje, które umożliwiają rozpoczęcie pracy z elementami stylizacji w sposób funkcjonalny i wielokrotnego użytku. W tym artykule nauczysz się podstaw stylizowanych komponentów i jak poprawnie zastosować je w swoich aplikacjach React. Powinieneś wcześniej popracować nad Reactem przed przejściem do tego samouczka. Jeśli szukasz różnych opcji stylizacji komponentów Reacta, możesz zajrzeć do naszego poprzedniego wpisu na ten temat.
U podstaw CSS leży możliwość kierowania na dowolny element HTML — globalnie — bez względu na jego pozycję w drzewie DOM. Może to stanowić przeszkodę w przypadku używania z komponentami, ponieważ komponenty wymagają, w rozsądnym stopniu, kolokacji (tj. utrzymywania zasobów, takich jak stany i stylizacja) bliżej miejsca, w którym są używane (tzw. lokalizacja).
Według własnych słów Reacta, stylizowane komponenty są „ wizualnymi prymitywami dla komponentów ”, a ich celem jest zapewnienie nam elastycznego sposobu stylizowania komponentów. Rezultatem jest ścisłe powiązanie komponentów i ich stylów.
Uwaga: Stylizowane komponenty są dostępne zarówno dla React, jak i React Native i chociaż zdecydowanie powinieneś zapoznać się z przewodnikiem React Native, tutaj skupimy się na stylizowanych komponentach dla React.
Dlaczego stylizowane komponenty?
Oprócz pomocy w zakresie stylów komponenty ze stylami zawierają następujące funkcje:
- Automatyczne prefiksowanie dostawcy
Możesz użyć standardowych właściwości CSS, a stylizowane komponenty dodadzą prefiksy dostawcy, jeśli będą potrzebne. - Unikalne nazwy klas
Komponenty ze stylami są od siebie niezależne i nie musisz się martwić o ich nazwy, ponieważ biblioteka obsługuje to za Ciebie. - Eliminacja martwych stylów
Składniki ze stylami usuwają nieużywane style, nawet jeśli są zadeklarowane w kodzie. - i wiele więcej.
Instalacja
Instalowanie stylizowanych komponentów jest łatwe. Możesz to zrobić przez CDN lub za pomocą menedżera pakietów, takiego jak Yarn…
yarn add styled-components
… lub npm:
npm i styled-components
Nasze demo wykorzystuje aplikację create-react-app.
Zaczyna się
Być może pierwszą rzeczą, jaką zauważysz w przypadku stylizowanych komponentów, jest ich składnia, która może być zniechęcająca, jeśli nie rozumiesz magii kryjącej się za stylizowanymi komponentami. Krótko mówiąc, komponenty ze stylami używają literałów szablonów JavaScript, aby wypełnić lukę między komponentami a stylami. Tak więc, kiedy tworzysz komponent ze stylami, tak naprawdę tworzysz komponent React ze stylami. To wygląda tak:
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>; }
W tym StyledButton
jest składnikiem stylizowanym i będzie renderowany jako przycisk HTML z zawartymi stylami. styled
to wewnętrzna metoda narzędziowa, która przekształca stylizację z JavaScript na rzeczywisty CSS.
W surowym HTML i CSS otrzymalibyśmy to:
button { background-color: black; font-size: 32px; color: white; } <button> Login </button>
Jeśli styled components są komponentami React, czy możemy używać rekwizytów? Tak możemy.
Adaptacja na podstawie rekwizytów
Stylizowane komponenty są funkcjonalne , dzięki czemu możemy łatwo dynamicznie stylizować elementy. Załóżmy, że na naszej stronie mamy dwa rodzaje przycisków, jeden z czarnym tłem, a drugi niebieski. Nie musimy tworzyć dla nich dwóch stylizowanych komponentów; możemy dostosować ich stylizację na podstawie ich rekwizytów.
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> ) }
Ponieważ StyledButton
jest komponentem React, który akceptuje właściwości, możemy przypisać inny kolor tła na podstawie istnienia lub wartości właściwości bg
.
Zauważysz jednak, że nie nadaliśmy naszemu przyciskowi type
. Zróbmy to:
function Profile() { return ( <> <StyledButton bg="black" type="button"> Button A </StyledButton> <StyledButton bg="blue" type="submit" onClick={() => alert("clicked")}> Button B </StyledButton> </> ); }
Stylizowane komponenty mogą rozróżniać typy rekwizytów, które otrzymują. Wiedzą, że type
jest atrybutem HTML, więc faktycznie renderują <button type="button">Button A</button>
, używając właściwości bg
we własnym przetwarzaniu. Zwróć uwagę, jak dołączyliśmy również procedurę obsługi zdarzeń?
Skoro już jesteśmy przy atrybutach, rozszerzona składnia pozwala nam zarządzać właściwościami za pomocą konstruktora attrs
. Spójrz na to:
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"}; `;
Zauważ, że nie potrzebujemy trójki podczas ustawiania szerokości? To dlatego, że ustawiliśmy już dla niego domyślną width: props.width || "100%",
width: props.width || "100%",
. Ponadto użyliśmy niestandardowych właściwości CSS, ponieważ możemy!
Uwaga: Jeśli styled components są komponentami React i możemy przekazywać właściwości, to czy możemy również używać stanów? Konto biblioteki GitHub ma problem dotyczący tej właśnie sprawy.
Rozszerzanie stylów
Załóżmy, że pracujesz nad stroną docelową i ustawiłeś kontener na określoną maksymalną szerokość, aby wszystko było wyśrodkowane. Masz do tego StyledContainer
:
const StyledContainer = styled.section` max-width: 1024px; padding: 0 20px; margin: 0 auto; `;
Następnie odkrywasz, że potrzebujesz mniejszego pojemnika z wypełnieniem 10 pikseli po obu stronach zamiast 20 pikseli. Twoja pierwsza myśl może polegać na stworzeniu kolejnego komponentu ze stylami i miałbyś rację, ale nie zajęłoby to dużo czasu, zanim zorientujesz się, że powielasz style.
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; `;
Zanim przejdziesz dalej i utworzysz StyledSmallContainer
, jak w powyższym fragmencie, nauczmy się, jak ponownie używać i dziedziczyć style. To mniej więcej tak, jak działa operator 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> ); }
W swoim StyledSmallContainer
otrzymasz wszystkie style z StyledContainer
, ale wypełnienie zostanie zastąpione. Pamiętaj, że zwykle otrzymasz element sekcji renderowany dla StyledSmallContainer
, ponieważ to właśnie renderuje StyledContainer
. Ale to nie znaczy, że jest wyrzeźbione w kamieniu lub niezmienne.
„As” polimorficzna prop
Za pomocą właściwości as
polimorficznej możesz zamienić element końcowy , który jest renderowany. Jednym z przypadków użycia jest dziedziczenie stylów (jak w ostatnim przykładzie). Jeśli, na przykład, wolisz div
niż section
dla StyledSmallContainer
, możesz przekazać as
prop do swojego stylizowanego komponentu z wartością preferowanego elementu, na przykład:
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> ); }
Teraz StyledSmallContainer
będzie renderowany jako div
. Możesz nawet mieć niestandardowy komponent jako swoją wartość:
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> ); }
Nie bierz tego za pewnik.

Składnia podobna do SCSS
Preprocesor CSS Stylis umożliwia stylizowanym komponentom obsługę składni podobnej do SCSS, na przykład zagnieżdżanie:
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> ); }
Animacja
Komponenty ze stylami mają pomocnika keyframes
, który pomaga w konstruowaniu (wielokrotnego użytku) klatek kluczowych animacji. Zaletą jest to, że klatki kluczowe zostaną oddzielone od stylizowanych komponentów i będą mogły być eksportowane i ponownie używane w razie potrzeby.
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; `;
Globalna stylizacja
Chociaż pierwotnym celem CSS-in-JS i, co za tym idzie, stylizowanych komponentów, jest określenie zakresu stylów, możemy również wykorzystać globalny styl komponentów stylizowanych. Ponieważ pracujemy głównie ze stylami z zakresem, możesz pomyśleć, że to niezmienne ustawienie fabryczne, ale mylisz się. Pomyśl o tym: czym tak naprawdę jest scoping? Technicznie możliwe jest dla nas — w imię globalnej stylizacji — zrobienie czegoś podobnego do tego:
ReactDOM.render( <StyledApp> <App /> </StyledApp>, document.getElementById("root") );
Ale mamy już funkcję pomocniczą — createGlobalStyle
— której jedynym powodem istnienia jest globalna stylizacja. Dlaczego więc odmawiać mu odpowiedzialności?
Jedną z rzeczy, do których możemy użyć createGlobalStyle
, jest normalizacja CSS:
import {createGlobalStyle} from "styled-components"; const GlobalStyle = createGlobalStyle` /* Your css reset here */ `; // Use your GlobalStyle function App() { return ( <div> <GlobalStyle /> <Routes /> </div> ); }
Uwaga: Style utworzone za pomocą createGlobalStyle
nie akceptują żadnych elementów potomnych. Dowiedz się więcej w dokumentacji.
W tym momencie możesz się zastanawiać, dlaczego w ogóle powinniśmy zawracać sobie głowę używaniem metody createGlobalStlye
. Oto kilka powodów:
- Bez tego nie możemy celować w nic poza głównym renderowaniem (na przykład
html
,body
, itp.). -
createGlobalStyle
wstrzykuje style, ale nie renderuje żadnych rzeczywistych elementów. Jeśli przyjrzysz się uważnie ostatniemu przykładowi, zauważysz, że nie określiliśmy żadnego elementu HTML do renderowania. To jest fajne, ponieważ możemy nie potrzebować tego elementu. W końcu interesują nas style globalne. Celujemy w selektory, a nie w konkretne elementy. -
createGlobalStyle
nie jest objęty zakresem i może być renderowany w dowolnym miejscu naszej aplikacji i będzie miał zastosowanie, o ile znajduje się w DOM. Pomyśl o koncepcji , a nie o strukturze .
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> ); }
Jeśli myślisz o strukturze, app-title
nie powinien mieć stylu ustawionego w GlobalStyle
. Ale to nie działa w ten sposób. Gdziekolwiek wybierzesz renderowanie GlobalStyle
, zostanie on wstrzyknięty podczas renderowania twojego komponentu.
Bądź ostrożny : createGlobalStyles
będzie renderowany tylko wtedy, gdy jest w DOM.
Pomocnik CSS
Widzieliśmy już, jak dostosowywać style w oparciu o rekwizyty. A co gdybyśmy chcieli pójść trochę dalej? Pomaga w tym funkcja pomocnicza CSS. Załóżmy, że mamy dwa pola tekstowe ze stanami: puste i aktywne, każde w innym kolorze. Możemy to zrobić:
const StyledTextField = styled.input` color: ${(props) => (props.isEmpty ? "none" : "black")}; `;
Wszystko jest w porządku. Następnie, jeśli będziemy musieli dodać kolejny stan wypełnienia, musielibyśmy zmodyfikować nasze style:
const StyledTextField = styled.input` color: ${(props) => props.isEmpty ? "none" : props.active ? "purple" : "blue"}; `;
Teraz trójskładnikowa operacja staje się coraz bardziej złożona. Co się stanie, jeśli później dodamy inny stan do naszych pól wprowadzania tekstu? A co, jeśli chcemy nadać każdemu państwu dodatkowe style, inne niż kolor? Czy możesz sobie wyobrazić wciśnięcie stylów w trójskładnikową operację? Pomocnik css
przydaje się.
const StyledTextField = styled.input` width: 100%; height: 40px; ${(props) => (props.empty && css` color: none; backgroundcolor: white; `) || (props.active && css` color: black; backgroundcolor: whitesmoke; `)} `;
To, co zrobiliśmy, to poniekąd rozszerzyliśmy naszą trójskładnikową składnię, aby pomieścić więcej stylów oraz bardziej zrozumiałą i zorganizowaną składnię. Jeśli poprzednia instrukcja wydaje się błędna, to dlatego, że kod próbuje zrobić zbyt wiele. Cofnijmy się więc i doprecyzujmy:
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; `} `;
Nasze udoskonalenie dzieli stylizację na trzy różne łatwe do opanowania i łatwe do zrozumienia części. To wygrana.
Menedżer arkuszy stylów
Podobnie jak pomocnik CSS, StyleSheetManager
jest metodą pomocniczą do modyfikowania sposobu przetwarzania stylów. Wymaga pewnych właściwości — takich jak disableVendorPrefixes
(możesz sprawdzić pełną listę) — które pomagają zrezygnować z prefiksów dostawców z jego poddrzewa.
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
jest przekazywane jako właściwość do <StyleSheetManager>
. Tak więc, stylizowane komponenty opakowane przez <StyleSheetManager>
byłyby wyłączone, ale nie te w <StyledNav>
.
Łatwiejsze debugowanie
Kiedy przedstawiałem stylizowane komponenty jednemu z moich kolegów, jednym z ich narzekań było to, że trudno jest znaleźć wyrenderowany element w DOM – lub w React Developer Tools. Jest to jedna z wad stylizowanych komponentów: próbując zapewnić unikalne nazwy klas, przypisuje unikalne skróty do elementów, które są zagadkowe, ale sprawia, że displayName
jest czytelny, co ułatwia debugowanie.
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> ); }
Domyślnie stylizowane komponenty renderują LoginButton
jako <button class="LoginButton-xxxx xxxx">Login</button>
w DOM oraz jako LoginButton
w React Developer Tools, co ułatwia debugowanie. Możemy przełączyć wartość logiczną displayName
, jeśli nie chcemy tego zachowania. Wymaga to konfiguracji Babel.
Uwaga : w dokumentacji określony jest pakiet babel-plugin-styled-components
, a także plik konfiguracyjny .babelrc
. Problem polega na tym, że ponieważ używamy create-react-app
, nie możemy skonfigurować wielu rzeczy, chyba że wyrzucimy. Tu właśnie wkraczają makra Babel.
Musimy zainstalować babel-plugin-macros
za pomocą npm lub Yarn, a następnie utworzyć babel-plugin-macros.config.js
w katalogu głównym naszej aplikacji o treści:
module.exports = { styledComponents: { displayName: true, fileName: false, }, };
Gdy wartość fileName
jest odwrócona, displayName
zostanie poprzedzony nazwą pliku dla jeszcze większej precyzji.
Musimy teraz również zaimportować z macro
:
// Before import styled from "styled-components"; // After import styled from "styled-components/macro";
Wniosek
Teraz, gdy możesz programowo komponować swój CSS, nie nadużywaj wolności. Za to, co jest warte, postaraj się zachować zdrowy rozsądek w stylizowanych komponentach. Nie próbuj tworzyć ciężkich warunków warunkowych ani nie zakładaj, że każda rzecz powinna być komponentem stylizowanym. Nie należy też nadmiernie abstrahować, tworząc rodzące się stylizowane komponenty dla przypadków użycia, które tylko zgadujesz, są gdzieś tuż za rogiem.
Dalsze zasoby
- Dokumentacja, stylizowane komponenty
- „Tworzenie systemu komponentów wielokrotnego użytku za pomocą React.js i styled-components”, Lukas Gisder-Dube
- Wykorzystanie z Next.js
- Wykorzystanie z Gatsby