Styled components - wprowadzenie

Możliwe, że zwróciliście uwagę, że w poprzednich artykułach nie stosowałem standardowych tagów HTML tylko posługiwałem się jakimś dziwnym oznaczeniem z przedrostkiem “Styled.”. Dziś postaram się wyjaśnić skąd się ono wzięło, a co ważniejsze - dlaczego się pojawiło.

Stylowanie komponentów w React

Do stylowania komponentów można wykorzystać kilka technik. Poniżej znajdziecie ich listę, żeby ci bardziej zainteresowani wiedzieli, o czym mogą poczytać:

W artykule skupimy się jednak tylko na jednej z nich.

Dlaczego warto korzystać ze styled-components?

Jest szereg korzyści wynikających z korzystania ze wspomnianej biblioteki. Skupimy się jednak na tych najważniejszych, na które faktycznie warto poświęcić trochę czasu.

Warto zacząć od tego, że komponenty napisane z użyciem styled-components otrzymują unikatowe nazwy klas, do których przypisywane są style. Dzięki temu nie musimy się martwić o ich nakładanie się, co z kolei pozwala skupić się na samym kodzie zamiast pilnowania metodyk ułatwiających strukturyzowanie arkuszy stylów. Takie podejście znacznie ułatwia rozwój i utrzymanie kodu, a do tego zabezpiecza nas przed trudnymi do znalezienia błędami związanymi z nakładaniem się definicji stylów.

Kolejny kluczowy aspekt to prostota powiązania komponentu Reactowego ze stylem. Jako programiści kierujący się dobrymi praktykami zazwyczaj nie lubimy “wiązać” czegokolwiek w kodzie. Zaufajcie mi, że w tym wypadku nie jest to nic złego i ciężko jest zrobić sobie pod górkę. Co dokładniej mam na myśli, mówiąc o wiązaniu? Styled-components pozwala przekazywać wartości bezpośrednio do definicji stylów, wykorzystując do tego propsy. Oznacza to, że możemy przekazać przez nie dowolną wartość, a następnie wykorzystać ją w definicji stylu.

Wśród istotnych zalet wymieniłbym jeszcze dwie kwestie: ekspresyjność kodu i optymalizację.

Pierwsza z nich oznacza nic innego jak podniesienie czytelności kodu poprzez nadawanie konkretnych nazw konstrukcjom złożonym z tagów HTML. W skrócie - opisując przycisk do wysyłania formularza nie korzystamy z HTMLowego “button,” tylko możemy zdefiniować stałą SubmitButton, którą następnie wykorzystamy w naszym JSX’ie. W klasycznym HTMLu i CSSie podobny efekt często uzyskiwaliśmy poprzez użycie atrybutów id oraz klasy.

Druga kwestia natomiast dzieje się “pod spodem” i nie wymaga od nas dodatkowego wkładu. Optymalizacja polega na tym, że styled-components ładuje definicje stylów tylko dla komponentów obecnie renderowanych na ekranie. Od klasycznego podejścia różni się to tym, że jeżeli użytkownik nie zobaczy jednego z widoków, to nie będziemy ładować jego stylu. Korzystając z CSSa klient wchodząc na stronę musiał pobrać wszystkie definicje arkuszy stylów niezależnie od tego, czy odwiedził daną ścieżkę, czy też nie.

Abyście lepiej zrozumieli powyższe zalety, nieco dalej poprę je przykładami, ale najpierw…

Jestem styled… styled-component

Zanim pójdziemy dalej, najpierw zainstalujmy paczkę:

npm install --save styled-components

Jeżeli korzystacie z TypeScripta, będziecie też potrzebować definicje typów:
npm install --save @types/styled-components

A zatem, do kodu!

Dobrą praktyką jest wydzielanie stylów do osobnego pliku. Ja mam w zwyczaju nazywanie ich identycznie jak komponent, do którego się odnoszą - dodając przyrostek “.styles”. Przykładowo - komponent “Dashboard.jsx” i odnoszące się do niego style - “Dashboard.styles.js”.

A teraz stwórzmy swój pierwszy stylowany komponent!

import styled from 'styled-components';

export const Button = styled.button``;

Wierzcie lub nie, ale to już jest poprawny styled-component. Nie wnosi on co prawda nic do wyglądu naszego przycisku, ale pozwala nam korzystać z dobrodziejstw napisanych w poprzednich akapitach.

Zwróćcie uwagę, że zdefiniowałem stałą, którą wyeksportowałem. Będzie nam to potrzebne za chwilę, ale najpierw stwórzmy jeszcze jeden komponent - tym razem mający nieco więcej sensu. Będzie to kontener opakowujący cały nasz przykład:

export const Container = styled.div`
 display: flex;
 flex-direction: row;
 justify-content: center;
 align-items: center;
`;

Jak widzicie jest to prosty div, który będzie pilnował ułożenia naszych elementów wewnątrz siebie. Jeżeli nie znacie flexboxa, to bądźcie spokojni - na końcu artykułu na pewno znajdziecie linki, które warto sprawdzić, żeby się z nim zaznajomić.

Wykorzystajmy teraz nasze komponenty wewnątrz Reacta. Najpierw musimy je zaimportować:

import * as Styled from './App.styles';

A teraz wykorzystajmy je przy renderowaniu:

const App = () => {
 return (
   <Styled.Container>
     <Styled.Button>Wciśnij mnie!</Styled.Button>
   </Styled.Container>
 );
}

Zwróćcie uwagę, że przy takim wykorzystaniu styled-componentów dokładnie wiemy, które komponenty są przez nas ostylowane i łatwiej jest szukać definicji ich stylów (większość IDE pozwala na bezpośrednie przejście do definicji danego stylu).

Rozszerzanie istniejących komponentów

Jest jeszcze jedna rzecz, którą warto znać zaczynając pracę ze styled-components. Jest to możliwość rozszerzania istniejących komponentów. Tyczy się to zarówno tych zdefiniowanych przez nas samych, jak i komponentów pochodzących z zewnętrznych bibliotek. Jedynym warunkiem działania dla tych drugich jest zwracanie przez nie propsa className do elementu DOM.

Żeby to zobrazować najłatwiej będzie posłużyć się przykładem. Wykorzystajmy do tego zdefiniowany wyżej Button. Dodajmy do niego kilka nowych wartości, żebyśmy mogli zaobserwować, że rozszerzające go komponenty odziedziczą jego definicję.

export const Button = styled.button`
 border: 0;
 border-radius: 3px;
 font-weight: bold;
 padding: 1rem;
 margin: 1rem;

 :hover {
   filter: invert(25%);
 }
`;

Zauważcie, że bez najmniejszych problemów wykorzystaliśmy pseudo-klasę hover zagnieżdżając ją w naszym komponencie. Pomoże ona pokazać użytkownikowi, że dany przycisk jest interaktywny. Pójdźmy teraz krok dalej i rozszerzmy ten przycisk o dwa warianty kolorystyczne. Do osiągnięcia tego celu wykorzystamy nieco inną składnię niż ta, której używaliśmy dotychczas. Zamiast definiować styl przy pomocy "styled.nazwa elementu" skorzystamy z funkcji styled do której, jako parametr podamy nasz komponent. Zobaczcie sami:

export const BlueButton = styled(Button)`
 color: #fff;
 background: #00f;
`;

export const RedButton = styled(Button)`
 background: #f00;
`;

Wszystkie style zdefiniowane wewnątrz Button zostaną odziedziczone do BlueButton i RedButton, ponadto dodaliśmy im odpowiednio kolor czcionki i tła. Jeżeli w podstawowym komponencie mielibyśmy zdefiniowane jedno z tych pól, to zostałoby ono zastąpione wartością określoną wewnątrz komponentu dziedziczącego.

Powrót do teorii

Wrócę jeszcze na chwilę do zalet, które nakreśliłem na początku artykułu. Co do samej czytelności to jesteście w stanie sami stwierdzić, jak wypadają definicje komponentów w porównaniu z klasycznym CSSem. Chciałbym pokazać Wam, do jakiej postaci generowane są stworzone przez nas komponenty, żeby lepiej nakreślić pierwszą z zalet - ochronę przed nałożeniem się stylów. Poniżej znajdziecie zrzut ekranu prezentujący strukturę DOM naszego komponentu:

styled-markup

Zwróćcie uwagę na to, jak wyglądają klasy każdego z elementów. Zostały one wygenerowane przez styled-components, a do każdej z klas przypisane są napisane przez nas definicje. W ten sposób mamy pewność, że żaden ze stylów nie nałoży się z innym.

Jeżeli chodzi o pozostałe zalety opisane na początku artykułu to z przekazywania wartości przez propsy będziemy korzystać w kolejnym artykule, który pojawi się już w przyszłym tygodniu.

W kwestii optymalizacji natomiast, jeśli nie chcecie wierzyć mi na słowo (co szczerze Wam polecam ;) ) to jesteście w stanie bardzo łatwo sprawdzić ją sami. Wystarczy, że dodacie dowolny nowy komponent z użyciem styled-componentsów, ale nie wykorzystacie go w żadnym widoku. W celu weryfikacji uruchomcie aplikację, wejdźcie w narzędzia developerskie przeglądarki (polecam Firefox), a następnie w “Edytor stylów”. Znajdziecie tam listę wszystkich klas CSS jakimi posługuje się nasza aplikacja. Będą one miały dziwne nazwy - jak te na screenie powyżej. Nawet jeśli bardzo uważnie je przeszukacie, to jestem pewny, że nie znajdziecie tam definicji stylu niewykorzystanego komponentu.

Mała przestroga na koniec

Jakkolwiek byście nie korzystali ze styled-componentów, pamiętajcie, żeby nie definiować ich wewnątrz komponentów funkcyjnych (chyba, że wykorzystacie memoizację), ani wewnątrz metody render komponentów klasowych. Jeżeli to zrobicie, będą one definiowane przy każdym renderze, co może negatywnie wpłynąć na wydajność Waszej aplikacji. Najlepiej jednak trzymać się wyciągania stylów do osobnego pliku. W ten sposób będziecie mieli pewność, że sobie nie zaszkodzicie, a do tego zachowacie czystość kodu.

I w ten oto sposób zamykamy nasz wstęp do styled-componentów. Za tydzień przejdziemy do bardziej zaawansowanych zagadnień. Dziejszy przykład znajdziecie na repozytorium. Jeżeli dotychczas nie mieliście styczności z tym sposobem stylowania aplikacji, to szczerze polecam się z nim zmierzyć. Na pewno będzie warto!

Przydatne linki

Styled-components:

Flexbox:

Do góry