O stylach w React Native
Style w aplikacji React Native mogą bardzo szybko zmienić się w bałagan, jeśli od początku nie poświęcisz im choć trochę uwagi. Przede wszystkim style inline nie są dobrym rozwiązaniem. JSX już trochę miesza warstwy prezentacji i logiki, więc nie ma potrzeby dorzucania dodatkowo wielu obiektów ze stylami i dezorientowania siebie w przyszłości lub kogokolwiek, kto może potrzebować czytać i edytować Twój kod. Popularnym podejściem jest użycie wbudowanego arkusza stylów (StyleSheet) React Native i zadeklarowanie ich poza komponentem. Możesz także znaleźć inne dobre metody, takie jak styled-components, ale nie będziemy tutaj omawiać ich wszystkich. Zachęcam do eksperymentowania i znalezienia rozwiązania, które najlepiej Tobie pasuje.
StyleSheet można umieszczać w wielu różnych miejscach projektu. Najprostszą metodą jest przechowywanie ich w tym samym pliku co komponent. W takim przypadku nie musisz przełączać się między plikami podczas pracy nad nowym komponentem lub gdy musisz później coś znaleźć i zmienić. Wadą jest większy plik, ale jeśli utrzymasz proste komponenty, nie powinno to stanowić problemu.
Innym podejściem jest utworzenie osobnego pliku dla każdego z komponentów. Oddziela to logikę od Twoich stylów i sprawia, że Twoje pliki są krótkie. Pamiętaj, aby zachować uporządkowaną strukturę projektu, jeśli wybierzesz ten sposób. Dobrym zwyczajem jest nazywanie plików zawierających style tak samo jak komponent, któremu odpowiada i dodanie określenia „.styles”. Na przykład dla MyButton.js utwórz plik MyButton.styles.js. Możesz dodatkowo umieścić oba pliki w folderze nazwanym tak jak komponent, czyli w tym przykładzie myButton. Decyzja należy do Ciebie.
Może się okazać, że powtarzasz te same style dla różnych komponentów. Umieszczenie ich w osobnych plikach pozwala na ponowne wykorzystanie niektórych z nich, co jest zgodne z zasadą DRY. Pozostawia to jednak bardziej skomplikowaną strukturę i może wymagać nieco więcej wysiłku, aby zlokalizować źródło określonej reguły. Oczywiście pozostaje też kwestia nazewnictwa – takie podejście prawdopodobnie spowoduje powstanie wielu ogólnych plików „styles.js” tworzących zamęt w drzewie projektu.
To tylko duży skrót i moglibyśmy o tym dyskutować o wiele bardziej szczegółowo, ale jak już widać, temat nie jest tak prosty, jak się na początku wydaje. Wszystkie rozwiązania mają swoje plusy i minusy. Niezależnie od tego, co wybierzesz, utworzenie motywu w projekcie może znacznie poprawić jego łatwość utrzymania, nawet jeśli nie planujesz tworzyć wielu motywów do wyboru przez użytkownika, ale oczywiście pozostawia otwarte drzwi dla przyszłych ulepszeń. W tym artykule chciałbym pokazać, jak stworzyć własny prosty motyw, aby skorzystać z zasady DRY, jednocześnie umieszczając StyleSheets w tym samym pliku jako komponent, ponieważ jest to sposób, który najbardziej mi odpowiada.
Aplikacja demonstracyjna
Na potrzeby tego artykułu stworzyłem bardzo prostą i ograniczoną aplikację w React Native z tylko jednym ekranem. Nie jest on piękny, ale powinien pomóc zademonstrować główną koncepcję w ograniczonym środowisku bez innych rozpraszaczy, abyśmy mogli skupić się na bieżącym problemie. Po skonfigurowaniu środowiska zgodnie z dokumentacją dostępną pod tym adresem, zainicjowałem nowy projekt za pomocą poniższego polecenia.
npx react-native init ThemeDemoApp
Struktura projektu na początku przedstawia się następująco:
- App.js
- src
- screens
- HomeScreen.js
- styles
- theme
- screens
Ten ekran zawiera tylko kilka komponentów, ale mogłoby tutaj być wiele innych elementów o znacznie bardziej skomplikowanych stylach. Celem jest zademonstrowanie sposobu radzenia sobie ze stylizacją powtarzających się schematów, takich jak kolorowanie jakiejś części na zielono, jeśli ma przedstawiać swego rodzaju sukces. Prawdopodobnie będzie to powracający temat w Twojej aplikacji i nie jest to coś co można uzyskać poprzez wydzielenie kodu do pojedynczego komponentu.
Organizacja styli
Pierwszą rzeczą do zrobienia jest przeniesienie wspólnych wartości używanych w stylach aplikacji do jednego miejsca w projekcie. Pozwala to na zmianę pojedynczego wiersza kodu, aby od razu wpłynąć na wygląd całej aplikacji i zapobiegnie wszelkim niezgodnościom, które powstają w wyniku zwykłego zapomnienia o niektórych plikach. Stworzymy również jeden plik dla każdej kategorii stylizacji. Reguł związanych z czcionkami nie należy mieszać z kolorami ani marginesami.
W katalogu styles, w pliku colors.js można zadeklarować paletę dla całej aplikacji, a następnie przypisać te kolory do ich własnej funkcji. Nazewnictwo jest kluczowe i powinno raczej odzwierciedlać ogólne przeznaczenie koloru. Chodzi o to, aby nie być tutaj zbyt szczegółowym i nie deklarować nazw kolorów takich jak „HomeScreenTitle” (“Tytuł ekranu głównego”) lub „SettingsSecondRowText” (“Tekst w drugim rzędzie ustawień”). Wybierz takie nazwy jak “Title” (“Tytuł”) lub “SecondaryText” (“Tekst poboczny”), które będą dobrze pasowały do wszystkich komponentów, a nie tylko jednego. Przeniosę tutaj kolory z HomeScreen i zadeklaruję ich nazwy zgodnie z tą zasadą. Wystarczy, że stworzysz pojedynczy motyw, ale ja od razu stworzę wersję jasną i ciemną, między którymi będziemy mogli się później przełączać. Powstał również obiekt dla wspólnych kolorów, z których korzystają oba motywy.
Stworzyłem też plik z różnymi ustawieniami czcionek. To tylko niektóre predefiniowane warianty do użycia. Pomaga zapewnić spójność całej aplikacji. Jeśli w którymś momencie będziesz chciał zmienić rozmiar czcionki, możesz po prostu edytować ten pojedynczy plik. Możesz umieścić tutaj o wiele więcej opcji związanych z typografią, o ile planujesz, że będą one współużytkowane na wielu ekranach. Tego typu plików może powstać o wiele więcej, np. z wielkościami odstępów, marginesów itp.
Utworzenie “dostawcy” motywów (ang. theme provider)
Po zadeklarowaniu naszych stylów tym, czego potrzebujemy, jest tak zwany Provider (dostawca). Jest to komponent, który otacza naszą aplikację i zapewnia dostęp do właściwości motywu wszystkim komponentom potomnym. Do tego zadania użyjemy React Context. Jeśli nie znasz tego rozwiązania, możesz przeczytać o nim więcej w oficjalnej dokumentacji. Ponieważ mamy zarówno „jasne”, jak i „ciemne” zestawy kolorów, istnieje potrzeba ustalenia, którego użyć. Zostawię to na razie i wrócimy do tego później.
Musimy również zmodyfikować plik App.js i wprowadzić w nim dostawcę motywu.
Korzystanie z motywu za pomocą hooków
Teraz musimy znaleźć sposób na zastosowanie naszego motywu w komponentach. Moglibyśmy użyć Context.Consumer, ale lepiej jest skorzystać z najnowszych funkcji, takich jak hooki, więc wybierzemy wbudowany useContext. Nie chcemy powtarzać kodu odpowiedzialnego za odczytanie motywu w każdym komponencie, więc aby tego uniknąć, stworzymy niestandardowe hooki useTheme i useThemedStyles. Pierwszy z nich po prostu zapakuje skonfigurowany useContext i zwróci aktualny stan motywu, drugi pójdzie o krok dalej i wstawi motyw jako argument do dostarczonej funkcji, co z kolei pozwoli nam wykorzystać go podczas tworzenia arkuszy stylów.
Zamiast deklarować style w taki sposób:
Będziemy tworzyć taką funkcję:
Teraz możemy zaimplementować te hooki na ekranie głównym. Poniżej znajdziesz zaktualizowaną wersję pliku. Jak widać, użyłem useTheme do przekazywania kolorów bezpośrednio jako atrybutów (props), a także useThemedStyles do ogólnych styli. Nie zawsze istnieje potrzeba korzystania z obu, w większości scenariuszy prawdopodobnie użyjesz tylko drugiego. Ekran powinien wyglądać dokładnie tak samo jak poprzednio, ale już przy nieco większym projekcie zrobiłoby to ogromną różnicę. Łatwiej jest zarządzać konfiguracją, jeśli znajduje się ona w jednym miejscu.
Przełączanie motywów
Ostatnią częścią jest umożliwienie użytkownikowi przełączania się między dostępnymi motywami zgodnie z jego preferencjami. Pokażę tylko ograniczoną wersję tej funkcjonalności, tj. bez zapisywania ustawień, bo to zupełnie inna historia i nie jest to właściwie temat tego artykułu. Powinieneś tutaj zastosować np. Redux wraz z redux-persist, aby w pełni wykorzystać to podejście do organizowania swoich stylów w React Native. Pamiętaj, że chcę Ci tylko pokazać, jakie masz możliwości i co możesz osiągnąć, projektując w ten sposób swój motyw.
Ponieważ mam tylko jasne i ciemne motywy, umieszczę komponent Switch na ekranie głównym. Normalnie umieściłbym to na ekranie ustawień, ale staramy się ograniczyć złożoność do minimum. Stworzę również funkcję do przełączania motywów w moim dostawcy i ponownie umieszczę ją bezpośrednio w obiekcie motywu, aby wszystko było jak najmniej skomplikowane.
Podsumowanie
Refaktoryzacja całej aplikacji nie jest łatwym zadaniem, dlatego sposób, w jaki rozpoczniesz jej rozwój, będzie miał duży wpływ na Twój projekt w przyszłości. Zastanów się nad poświęceniem trochę więcej czasu w fazie przygotowań, a powinna ona przynieść korzyści w przyszłości. Mam nadzieję, że ten artykuł dostarczył przydatnych informacji i pomoże ci lepiej uporządkować twoje style. Cały kod aplikacji demo znajdziesz tutaj.