8 dobrych praktyk pracy w React.js

Ciężko mi już zliczyć ile projektów w React (…i jego ekosystemie) napisałem. Postanowiłem spisać praktyki, które wypracowałem i uważam za warte użycia w waszych projektach. A, że lubię konkrety, to od razu przejdźmy do pierwszej z nich.

Łukasz Ostrowski. Frontend developer w Ideamotive. Geek, fan Reacta i nowości w światku IT. Interesuje się wszystkim co związane z branżą, od rekrutacji, przez programowanie, po project management i zarządzanie firmą.


1. Unikanie lokalnego stanu

Naturalne zdaje się posiadanie pewnej architektury stanu, co w projektach Reactowych przeważnie oznacza Redux, MobX lub jakąś inną, mniej popularną implementację. Ważne jest, że przeniesienie całego stanu w jedno miejsce sprawia, że aplikacja staje się przewidywalna i łatwa do debugowania, a gdy nowy programista potrzebuje dowiedzieć się skąd pochodzą dane, zawsze wie gdzie szukać.

Używanie stanu Reactowego (hook useState lub state w class komponentach) ma swoje uzasadnienia, jednak w większości przypadków powoduje komplikacje cyklu rerenderowania, tworzy tzw. side effecty i w efekcie mamy dwa różne miejsca gdzie tego stanu szukać.

Jeżeli lokalnego stanu używam, to w bardzo określonych przypadkach – np w formularzach (korzystając z abstrakcji Formika) czy niektórych komponentów UI, które mogą być samodzielne – accordiony czy karuzele.

2. Abstrakcja stanu od widoku

Gdy już użyję stanu to zawsze staram się oddzielić go od dumb componentu. Przykładowo, jeżeli mam komponent accordion i potrzebuję wiedzieć, która sekcja jest otwarta, i tak tworzę czysty komponent (controlled), który w propsach dostaje aktualnie otwartą sekcję (np. ID) oraz callback do otwarcia pozostałych. Następnie tworzę HOC posiadający stan (przeważnie hook useState) i owija nim Accordion, tworząc samodzielny komponent.

Po co ta gimnastyka?

Po pierwsze, dużo łatwiej testować te dwie funkcje osobno niż razem. Na końcu test „integracyjny” oczywiście można przeprowadzić na owrapowanym komponencie.

Po drugie, jest spora szansa, że wraz z nadejściem nowych wymagań biznesowych, sekcje będą otwarte np. zależnie od stanu URL czy innych komponentów, co pozwala mi ponownie użyć czystego komponentu bez modyfikowania tego stanowego.

3. Używanie selectorów w Reduxie za każdym razem

Standardowe (tutorialowe) zastosowania funkcji mapStateToProps bezpośrednio w warstwie komponentu tworzy mapowanie stanu na propsy, tworząc tight coupling, a proces mapowania powtarzany jest za każdym razem gdy potrzeba danych.

Jeżeli zmienimy strukturę naszego store Reduxa, musimy zmienić to w każdym mapStateToProps, którego użyjemy, niezbyt DRY.

Dlatego nawet do prostych mapowań zawsze używam selectorów (Reselect, memoize-one), nawet jeżeli jest to prosta przelotka (x => x). Selectory dają dodatkowo… nazwę, która mówi nam ładnie i klarownie co pobieramy (getDistanceFormattedInMeters itd). Oczywiście nie trzeba mówić o zaletach memoizacji, która zapobiega niepotrzebnym rerenderom poprzez cache wyników.

4. Używam Typescript

Jest to uniwersalna dobra praktyka i nie będę się specjalnie nad zaletami TS rozwodził. W kontekście samego Reacta wspomnę tylko o dużo przyjemniejszym i „naturalnym” typowaniu propsów niż prop-types. Ponadto można otypować jakie komponenty można przekazać do HOC-a.

5. Obiekt zamiast tablicy w Reduxowym modelu danych

Przez długi czas moje struktury danych w Reduxie były raczej tablicami, np. { posts: Post[] }, jednak wraz ze skalowaniem aplikacji zauważyłem dużo większe benefity w strukturze klucz-wartość, np. { postsById: { [postId: number]: Post } }. Struktura ta pozwala dużo łatwiej aktualizować i pobierać dane, bez kosztownych operacji na tablicach.

Finalnie używam selektora, który mapuje dane do tablic, przykładowo:

6. Przekazuję komponenty jako propsy

Technika ta szczególnie przydaje się do tworzenia uniwersalnych, reużywalnych bibliotek UI (zerknij na Material UI). Standardowym scenariuszem jest stworzenie komponentu na bazie określonego designu, a potem wraz z nowymi wymaganiami zaczynają się komplikacje – ściany ifów, ogromne kombinacje propsów i tworzenie wielu różnych wariantów.

Dlatego staram się dostrzec w komponentach coś w rodzaju slotów. Gdy widzę Button z ikoną po lewej (lub prawej) stronie, tworzę komponent który przyjmuje opcjonalny ReactNode w propsach leftSlot i rightSlot (lub leftIcon i rightIcon, to już zależy).

Późniejsze użycie jest bardzo przyjemne, bo przy użyciu kompozycji tworzę sobie różne reużywalne warianty, przykładowo:

Tego typu przykładów mam dziesiątki i nie trzeba wcale pisać reużywalnej biblioteki by warto było skupić się na takim podejściu. Przede wszystkim zapewnia nam to większe bezpieczeństwo w przypadku wszelkich zmian, a te w większych projektach są nieuniknione.

7. Wszelkie efekty staram się przekazywać przez Reduxa

W standardowej aplikacji typowym efektem są wszelkie requesty HTTP. Piszę serwisy, które odpowiadają za przesyłanie danych, jednak w żadnym wypadku nie używam serwisów w komponentach. Używam do tego efektów, a więc komponenty łączą się jedynie z Reduxem. Efekty zwracają przeważnie Promise-y, a te obsługuję zarówno w modelu (wywołanie reducera, zapisanie danych), jak i później w komponencie (zareagowanie na wynik).

Tego typu reguła gwarantuje, że wszelkich side effectów mogę szukać w modelu, zamiast rozprzestrzenionych po całej aplikacji na różnych warstwach.

8. Tworzę moduły domenowe

Nie ma dla mnie nic gorszego niż podział na wielkie foldery typu components, containers, actions, reducers etc. To rozwiązanie się nie skaluje, więc staram się tworzyć moduły związane z określonymi domenami. Nie jest to łatwe, a czasem, jeśli nie znamy dobrze projektu (lub zachodzą w nim częste zmiany) możemy narazić się na dodatkowy refactoring. Jednak warto jest tworzyć modułowe struktury, tak jak robi to Angular (pisałem o tym jak Angular pomógł mi stać się lepszym React developerem). Warto spojrzeć na Ducks.

Typowa zawartość modułu to interfejsy ze strukturami danych, serwisy, modele (store Redux), strony/widoki oraz komponenty związane ściśle z tymi domenami.


Artykuł został pierwotnie opublikowany na ostrowski.ninja. Zdjęcie główne artykułu pochodzi z unsplash.com.

Patronujemy

 
 
Polecamy
Standard OAuth2 inspiracją dla rozwiązań bezpieczeństwa w Polish API