Zdjęcie artykułu

Jak tworzyć skalowalne i reużywalne komponenty w React

6m
wzorce
komponenty
refaktor

Intro

Zrobimy review obrzydliwego komponentu "Comments", a następnie wykorzystamy wzorce takie jak "slot" czy "function as a child" do oczyszczenia kodu.

Najważniejszy jest kontekst

Zanim zagłębimy się w treść artykułu, istotne jest podkreślenie kontekstu. Naszym głównym celem jest stworzenie maksymalnie reużywalnych komponentów. Przykłady przez które przejdziemy, są szczególnie wartościowe, gdy celem jest rozwinięcie wszechstronnej biblioteki komponentów. W przypadku typowego kodu aplikacji moze to być overkill.

Słów kilka na temat komponentów

Najlepszy komponent to taki, który może być wykorzystany w dowolnej aplikacji. Dodatkowo taki komponent powinien mieć:

  • proste i łatwe do zrozumienia API,
  • definicje typów w TypeScript lub w innej technologii,
  • możliwość ustawienia zachowania w oparciu o przekazane parametry,
  • testy.

Tak jak u ludzi, nasze komponenty mogą mieć objawy, które wskazują na to, że są po prostu "chore". Tak, bardzo lubię metafory... Tłumacząc prostymi słowami, najlepszy komponent to taki, który może być użyty w innej aplikacji bez żadnych dodatkowych zmian - w zasadzie ctrl+c i ctrl+v i działa.

Ta prosta definicja jest mojego autorstwa - chcę jedynie pokazać, jak istotne jest tworzenie komponentów odseparowanych od domeny i specyfiki aplikacji.

Przykład komponentu, który jest powiązany z domeną aplikacji i jest źle zaprojektowany

Szanujcie swój cenny czas. Jeżeli spędzamy czas na implementacji komponentu i zrobimy to źle, nigdy więcej nie będziemy mogli wykorzystać go bez większych zmian, a przecież nie o to nam chodzi - nasz czas jest bardzo cenny.

Spójrzmy na poniższy komponent. Zaznaczyłem symbolem ❌ fragmenty, które są "złe". Później poprawimy te części.

Ładowanie

A tu przykład potencjalnego użycia:

Ładowanie

Po pierwsze, musimy zrozumieć jakie role powinien spełniać nasz komponent. Powinien wyświetlić listę komentarzy oraz header i footer.

Musimy wprowadzić zmiany aby rozdzielić nasz komponent od domeny aplikacji. Możemy to osiągnąć wykorzystując techniki takie jak content projection czy function as a child.

Stwórzmy więc nowy komponent - List. Dlaczego potrzebujemy nowego? Zaraz to wytłumaczymy ☜(゚ヮ゚☜).

Projektujemy reużywalny i rozszerzalny komponent List

Musimy zaimplementować nowy komponent ponieważ nie chcemy zepsuć aktualnie zaimplementowanego komponentu Comments. Najpierw stworzymy komponent List, upewnimy się, że działa prawidłowo, a następnie wykorzystamy go wewnątrz komponentu Comments.

Przykłady użycia:

Ładowanie

Ładowanie

Ładowanie

Dodatkowo przekazaliśmy typ Comment do komponentu List, aby wskazać kształt modelu na którym będzie on operował. Teraz jeżeli będziemy chcieli odnieść się do właściwości dowolnego komentarza, a ona nie będzie zdefiniowana lub będzie miała zły typ - TypeScript będzie się darł jak stara pościel.

Tworzymy definicje typów dla komponentu List

Zazwyczaj zaczynam swoją pracę od stworzenia definicji typów w oddzielnym pliku. Dzięki temu często wyłapuję nieścisłości na etapie modelowania. Również, daje mi to możliwość skorzystania z typów przy implementowaniu innego wariantu komponentu, jak i również, mogę skorzystać z typów w komponencie rodzica, który korzysta z komponentu List.

Dodatkowo taka separacja jest o wiele bardziej czytelna.

Ładowanie

Użyliśmy typu generycznego - T oraz type constraints - extends w celu weryfikacji przekazanego typu generycznego. Musi mieć co najmniej id oraz content.

Kolejna ciekawą rzeczą jest ListItem - jest to po prostu komponent, który ma zwrócić ReactNode.

W tym momencie, wprowadzony zapis może wydawać Ci się trochę dziwny. Nie martw się. Zaraz wszystko się wyjaśni (~ ̄(OO) ̄)ブ!

Implementujemy komponent List w oparciu o typy

Spójrz na poniższy kod. Zaimportowaliśmy stworzone typy i dopisaliśmy implementacje spełniając zawarty "kontrakt" z TypeScript. Kod jest znacznie krótszy niż początkowa implementacja oraz oferuje więcej możliwości.

Ładowanie

Teraz zwróć uwagę na to - możemy stworzyć tyle wariantów komponentu Comments ile tylko dusza zapragnie. Ten przykład pokazuje jak świetnie sprawdza się kompozycja w React.

Jak wykorzystać stworzony kod? Widzieliśmy już to wcześniej, ale sprawdźmy raz jeszcze dla przypomnienia.

Ładowanie

Ostatnie poprawki - dodajemy nowe możliwości do komponentu List

No dobra, ale dalej nie możemy przekazać własnej klasy, eventów czy dowolnych, innych właściwości. Aby to uzyskać skorzystajmy z definicji typów, która jest dostępna w bibliotece React i rozszerzmy nasze typy.

Ładowanie

Ostatnia zmiana i mamy to! Musimy wykorzystać dwa operatory: spread oraz rest. Pierwszy posłuży do przypisania dowolnych, przekazanych propsów do zmiennej props, a drugi to umieszczenia ich w znaczniku div.

Ładowanie

Teraz możemy przekazać nasze propsy plus wszystko, co można przekazać do znacznika div.

Ładowanie

Pominąłem implementację testów ponieważ ten artykuł nie jest o tym. Jeżeli interesuje Cię ten temat, to zapraszam do tego kursu - React testing spellbook.

Cały przykład na platformie CodeSandbox

Przemyślenia i wnioski

Wydaje mi się, że rezultat jest prosty i oczywisty - możemy tworzyć tyle wariantów komponentu Comments ile chcemy. Co ciekawe, możemy tworzyć inne komponenty - Posts, Users i tak dalej.

Teraz już wiesz jak korzystać z technik slot pattern (implementacja "header" oraz "footer") oraz function as a child (implementacja wyświetlania dowolnego elementu listy).

Jeżeli lubisz podobne artykuły do polecam serdecznie:

Wizualizacja danych na osi czasu - Creating React timeline component.

Tworzymy komponent do wyświetlania kodu - React code snippet component.

Jestem z tych, którzy publikują codziennie!

Mam nadzieję, że mój wpis Ci się spodobał. Jeżeli tak jest, to zapraszam Cię na mój LinkedIn, gdzie publikuję codziennie.

Komentarze

Przejrzyj komentarze artykułu i dodaj swoją opinię.

stworzono: 11-08-2023
zmieniono: 12-06-2023