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.
Komentarze
Przejrzyj komentarze artykułu i dodaj swoją opinię.