Strona główna / Artykuły / Zredukowanie React Prop-Drilling oraz God Components do niezbędnego rozmiaru

Zredukowanie React Prop-Drilling oraz God Components do niezbędnego rozmiaru

Naucz się siedmiu konkretnych wzorców refaktoryzacji, które umożliwiają rozbicie rozbudowanych komponentów React poprzez izolację stanu, pobierania danych, uprawnień oraz logiki ładowania, zamiast jedynie dzielenia plików.

3299 słów

Był taki okres, gdy złożoność React oceniano wyłącznie na podstawie liczby wierszy kodu. Gdy komponent przekraczał trzysta wierszy, pasek narzędzi przenoszono do osobnego pliku. Gdy liczba wierszy osiągała czterysta, wyodrębniano również tabelę. Plik najwyższego poziomu kurczył się, ale sama funkcjonalność nie stawała się łatwiejsza do zrozumienia. Komponent nadrzędny nadal zawierał wszystkie żądania sieciowe, każdą okienko dialogowe, rozproszone pola formularza wraz z ich stanem walidacji, sprawdzania uprawnień aktualnego użytkownika oraz zmieniające się stany ekranu podczas ładowania, edytowania, zapisywania i czasami awarii.

W rzeczywistości doszło do przeorganizowania elementów, bez żadnej zmiany w tym, kto jest właścicielem „domu”.

To rozróżnienie warto przyjąć, ponieważ sama wielkość nie jest wrogiem. Komponent może uzasadnienie mieć dużą objętość, jeśli reprezentuje jedną, spójną część interfejsu użytkownika. Intensywny edytor, panel sterowania lub strona z raportami mogą słusznie zawierać dużo kodu markup. Problemy pojawiają się wtedy, gdy jeden komponent staje się jedynym miejscem, gdzie podejmowane są całkowicie niepowiązane decyzje.

Żaden z poniższych wzorców nie jest z natury złym rozwiązaniem w React. Każdy z nich ma uzasadnione zastosowanie w mniejszym lub lepiej zaplanowanym kontekście. Stają się problematyczne dopiero wtedy, gdy pojawiają się wielokrotnie w dużych komponentach, ponieważ każde takie wystąpienie zwiększa powiązania między elementami, powiększa obszar dotknięty ponownymi renderingami i zmusza do uwzględniania całego ekranu przy każdej przyszłej zmianie.

Usunięcie ich nie pozostawia ci stosu bezsensownych, małych komponentów. Zamiast tego uzyskujesz jaśniejszą strukturę dotyczącą stanu, pobierania danych, interakcji oraz renderowania. Kod staje się łatwiejszy do modyfikacji, ponieważ mniej elementów funkcjonalności może przypadkowo wpływać na siebie nawzajem.

1. Komponenty, które przekazały całą funkcjonalność w dół

Jednym z nawyków, które często występują na większych ekranach, jest włączanie dużych obiektów oraz długich list funkcji zwrotnych do niemal każdego komponentu potomnego.

Załóżmy tabelę z wynikami, która otrzymuje aktualnego użytkownika, pełny obiekt uprawnień, aktywne filtry, wybrany wiersz, flagę ładowania, kilka funkcji mutacji, narzędzia do pokazywania okien modalnych oraz obsługę powiadomień — plus kilka wartości, których nigdy bezpośrednio nie dotyka. Tabela ta następnie przekazuje część z tych danych bezpośrednio do komponentów wierszy, które z kolei przekazują je dalej do poszczególnych komórek i przycisków.

Gdy spojrzymy na drzewo plików, wszystko wydaje się dobrze podzielone. Jednak rzeczywisty przepływ danych pomiędzy komponentami pokazuje inny obraz: każdy komponent potomny nadal jest połączony z całym funkcjonalnością.

To powoduje dwa istotne problemy. Po pierwsze, pogarsza się zrozumiałość kodu. Trudno jest pracować z komponentem przyjmującym piętnaście parametrów, ponieważ jego rzeczywiste zadanie jest ukryte pod szczegółami należącymi właściwie do jego komponentu nadrzędnego. Po drugie, zmiany rozprzestrzeniają się na inne elementy. Przenazwanie pojedynczego pola uprawnień lub niewielka modyfikacja sygnatury funkcji działania oznacza konieczność edytowania kilku warstw komponentów, które od początku nie miały żadnej rzeczywistej kontroli nad tym zachowaniem.

Rozwiązaniem jest zastąpienie szerokich parametrów opisujących funkcjonalności węższymi kontraktami, skupionymi na tym, za co właściwie odpowiada każdy komponent potomny. Tabela potrzebuje jedynie wierszy, stanu wyboru oraz funkcji typu onRowSelected. Menu działań wymaga tylko konkretnych działań dostępnych dla danego rekordu – a nie całego modelu uprawnień wraz z każdą istniejącą funkcją mutacji.

Aby to osiągnąć, zazwyczaj trzeba najpierw stworzyć mały model widoku lub model działania przed renderowaniem. Ten dodatkowy krok przygotowawczy jest cenny, ponieważ zmusza komponent nadrzędny do przekształcenia surowego stanu aplikacji w prostszy, specjalnie zaprojektowany interfejs przed przekazaniem jakichkolwiek danych.

Celem nie jest w żadnym wypadku minimalizowanie liczby właściwości wyłącznie dla samej przyjemności. Prawdziwą korzyścią jest to, że komponenty potomne przestają musieć rozumieć, jak funkcjonuje cała strona. Wystarczy im wiedzieć, jakie dane należy wyświetlić i jakie informacje przekazać w górę.

Komponent duży staje się kruchy w momencie, gdy każdy jego potomek przenosi fragment całego wewnętrznego świata nadrzędnego komponentu. Wąskie, specjalnie opracowane umowy pozwalają poszczególnym elementom interfejsu rozwijać się niezależnie, zamiast angażować całą funkcjonalność we wszystkie rozmowy.

2. Nowe obiekty i funkcje tworzone przy każdym renderowaniu

Każdy komponent React naturalnie wytwarza nowe wartości podczas renderowania. Przekazujesz obiekt opcji do potomnego komponentu, filtrowasz tablicę lub piszesz obsługę zdarzeń w formie inline. Przez większość czasu nic z tego nie ma znaczenia.

Jednak w głębokim drzewie komponentów świeżo utworzony referencja może w tajemnicy zakłócić mechanizm memoizacji na kilka poziomów poniżej komponentu, który ją stworzył.

Rozważmy komponent potomny zbudowany za pomocą memo: nadal renderuje się ponownie za każdym razem, gdy obiekt, który otrzymuje, ma nową tożsamość przy każdym przekazaniu do rodzica, nawet jeśli rzeczywiste wartości pól w tym obiekcie nigdy się nie zmieniły. Efekt jest restartowany, ponieważ jego tablica zależności zawiera nowo utworzony obiekt konfiguracji. Tabela przelicza swoje kolumny, ponieważ definicje kolumn zostały odbudowane po zmianie jakiegoś zupełnie niepowiązanego stanu modalnego.

Kod może wyglądać na całkowicie stabilny, ukrywając ten problem:

<ResultsTable
  columns={[
    { key: "name", label: "Name" },
    { key: "status", label: "Status" },
  ]}
  options={{
    selectable: true,
    compact: false,
  }}
/>

Z punktu widzenia JavaScriptu zarówno tablica, jak i obiekty w niej zawarte są zupełnie nowe przy każdym renderowaniu. To, czy faktycznie powoduje to problemy, zależy wyłącznie od tego, co je używa.

Uciekanie się do useMemo i useCallback jako uniwersalnego rozwiązania również nie jest odpowiedzią. Otaczanie wszystkiego mechanizmem memoizacji dodaje jedynie kolejny poziom złożoności oraz problemów z tablicami zależności. Lepszym punktem wyjścia jest zastanowienie się, czy wartość w ogóle musi być tworzona w trakcie renderowania.

Konfiguracja statyczna może zostać całkowicie przeniesiona poza komponent. Konfiguracja, która zmienia się sporadycznie, może trafić do dedykowanego hooka. Gdy obiekt istnieje wyłącznie po to, by połączyć kilka prymitywów, przekazywanie ich bezpośrednio jest zazwyczaj prostsze. Obsługi wydarzeń wymagają użycia useCallback tylko wtedy, gdy ich tożsamość faktycznie ma znaczenie – np. przy subskrypcjach, zapisanych dzieciach komponentów lub kosztownych ponownych obliczeniach poniżej w drzewie komponentów.

Celem nie jest nigdy czystość referencyjna dla samej jej przyjemności. Tworzenie małych wartości podczas renderowania jest normalne i zazwyczaj nieszkodliwe. Uważność należy zachować tylko w przypadkach, gdy tożsamość referencji odgrywa istotną rolę w innych częściach drzewa komponentów.

W dużym komponencie pojedyncza, niewielka aktualizacja stanu może doprowadzić do ponownego renderowania elementu nadrzędnego i przy tym wygenerować całą serię wartości. Jeśli każde dziecko traktuje zmienioną referencję jako zmienione dane, jedna mała lokalna aktualizacja przekształca się w unieważnienie całej strony.

3. Usunięcie pojedynczego zapytania, które napędzało cały ekran

Duże strony często zaczynają się od pojedynczej prośby o pobranie wszystkiego, czego interfejs może potrzebować. Ta jedna odpowiedź zawiera metryki podsumowawcze, wiersze tabeli, filtry, powiązane rekordy, dane uprawnień, najnowszą historię, a nawet pola wymagane tylko przez modale, których użytkownik może nigdy nie otworzyć.

Na pierwszy rzut oka ten podejście wydaje się efektywne, ponieważ ekran ma dokładnie jeden stan ładowania i jedno oczywiste źródło danych. Jednakże łączy ono również każdą część strony z najwolniejszym, najmniej niezawodnym elementem tej odpowiedzi.

Jeśli usługa historii zawiedzie, główna tabela może w ogóle się nie wyświetlić. Duże zbiory powiązane z nią zwiększają początkową objętość danych nawet w przypadku użytkowników, którzy nigdy nie otwierają panelu, który je wymaga. Ponadto odświeżenie tylko jednej sekcji zmusza do pełnego załadunku strony, ponieważ cała strona korzysta z jednej granicy danych.

Lepszym rozwiązaniem jest zastąpienie tych żądań o rozmiar całej strony granicami danych odpowiadającymi faktycznym widocznym elementom ekranu. Najpierw ładowane jest główne treści, a drugorzędne panele pobierają swoje dane dopiero wtedy, gdy stają się istotne. Drogie szczegóły są pobierane dopiero w momencie, gdy użytkownik otwiera konkretny rekord, zamiast być domyślnie zawierane we wszystkich wierszach.

To nie oznacza konieczności uruchamiania połączenia sieciowego dla każdego błahego elementu interfejsu. Nadmierne stosowanie takiego rozdzielenia powoduje własne problemy — proste sekwencje zapytań, duplikowane wywołania oraz niestabilne stany ładowania. Granicą, która naprawdę ma znaczenie, jest zazwyczaj obszar posiadający własny cykl życia oraz własną definicję tego, co oznacza „niepowodzenie”.

Element podsumowujący i dziennik audytu nie muszą działać pomyślnie lub zawodzić jako jedna całość. Tabela i rzadko używany panel edytora nie muszą dzielić się tym samym początkowym pakietem danych. Gdy te elementy zostaną rozdzielone, strona może pozostać funkcjonalna nawet wtedy, gdy jeden z opcjonalnych elementów przestanie działać.

Sam komponent staje się również znacznie łatwiejszy do zrozumienia, ponieważ nie musi już modelować jednej ogromnej, rozbudowanej struktury odpowiedzi. Każdy obszar otrzymuje dokładnie te dane, których potrzebuje, a unieważnianie pamięci podręcznej może dotyczyć wyłącznie tych rekordów, które uległy zmianie, zamiast całej strony.

Komponenty duże mają tendencję do stawania się kruche, właśnie wtedy, gdy ich model danych jest kształtowany przez wygodę na poziomie strony, a nie przez naturalny cykl życia poszczególnych elementów w nich zawartych.

4. Usuwanie komponentów ogólnych kontrolowanych przez dziesiątki flag

Powszechnym pierwszym instynktem w celu uniknięcia duplikacji jest tworzenie skrajnie konfigurowalnych komponentów. Jeden panel może być dostępny do wyszukiwania, selekcji, paginacji, edycji, eksportu oraz składania – wszystko to kontrolowane za pomocą coraz większej liczby właściwości logicznych.

Wydaje się, że można to ponownie wykorzystać, ponieważ obejmuje szeroki zakres scenariuszy. W rzeczywistości każdy nowy ekran oznacza jedynie dodanie kolejnego warunku do tej listy.

<DataPanel
  searchable
  selectable
  showToolbar
  allowExport={canExport}
  inlineEdit={mode === "admin"}
  compact={isInsideModal}
  hidePagination={rows.length < 20}
  stickyHeader={!isMobile}
/>

Prawdziwym problemem nie jest sama liczba właściwości. Flagi oddziałują na siebie w nieprzewidywalny sposób. Edycja w linii zachowuje się inaczej po aktywacji trybu wyboru. Kompaktowa rozkładka wymaga własnej logiki paska narzędzi. Nagłówek przyklejony do góry łamie się wewnątrz określonego kontenera z przekroczeniem rozmiaru. Liczba możliwych kombinacji flag rośnie znacznie szybciej, niż ktokolwiek jest w stanie je faktycznie przetestować.

Komponent cicho zamienia się w drugą aplikację ukrytą wewnątrz pierwszej.

Zastąpienie tego podejścia opartego na flagach o korzystanie z mniejszych, składalnych elementów oznacza faworyzowanie takich rozwiązań, a także tworzenie wyraźnych wariantów oddzielnie tam, gdzie różnice są znaczne. Tabela może być łączona z paskiem narzędzi, kontrolką paginacji oraz mechanizmem do wyboru elementów w zależności od potrzeb. Edytowalna tabela może istnieć jako odrębna funkcjonalność, a nie być jedynie kolejną opcją logiczną ukrytą wewnątrz generycznego komponentu tabeli.

Gdy dwa interfejsy dzielą rzeczywistą, wspólną strukturę, ta struktura jest wykorzystywana bezpośrednio. Gdy natomiast tylko przypominają się na ekranie, ale realizują różne procesy pracy, przymuszanie ich do korzystania z jednej wspólnej abstrakcji traci sens.

To powoduje ponowne pojawienie się niektórych duplikatów, które wcześniej były ukryte, co na początku może wydawać się krokiem w tył. W praktyce takie duplikaty są zazwyczaj o wiele tańsze niż architektura rozgałęzień, którą zastępują. Dwa małe, oddzielne komponenty mogą się zmieniać niezależnie, bez konieczności weryfikowania każdej edycji pod kątem dziesiątek niepowiązanych kombinacji flag.

Ponowne wykorzystanie się opłaca, gdy chroni pojedynczą, stabilną umowę. Staje się ryzykowne w momencie, gdy osiąga się je poprzez rozszerzenie jednego komponentu w celu potajemnego reprezentowania kilku różnych produktów jednocześnie.

5. Usuwanie stanu modalu z strony, która go otworzyła

Wielkie komponenty często pełnią rolę menedżerów modali. Śledzą, czy dany dialog jest otwarty, który rekord aktualnie edytuje, na jakim etapie procesu się znajduje, czy jest w trakcie zapisywania oraz jaki błąd ostatnio pojawił się.

Często sama strona zawiera jedynie jedną linię, która uruchamia otwarcie modalu, a mimo to odpowiada za cały cykl życia tego okna dialogowego.

To powoduje, że stan pozostaje aktywny nawet wtedy, gdy okno dialogowe jest całkowicie niewidoczne. Poprawne zamknięcie oznacza usunięcie wybranych rekordów, wartości z pól roboczych, błędów walidacji oraz zaległych żądań, wszystko to w określonej kolejności. Otwarcie innego okna dialogowego później niesie ryzyko przypadkowego ponownego użycia pozostałych wartości z wcześniej otwartego okna.

Traktowanie każdego złożonego okna dialogowego jako odrębnej funkcji, a nie jako warunkowego elementu JSX dodanego do strony, zmienia tę dynamikę. Zadaniem strony staje się określenie, na który rekord użytkownik zamierza podjąć działanie. Samo okno dialogowe odpowiada za dane robocze, logikę walidacji, wewnętrzne kroki oraz cykl życia przesyłania danych.

W niektórych przypadkach wystarczy po prostu montować okno dialogowe tylko wtedy, gdy jest faktycznie otwarte, aby automatycznie sformatować jego tymczasowy stan. W innych sytuacjach stan musi przetrwać po zamknięciu, więc przechodzi on do dedykowanego właściciela projektu zamiast pozostać powiązanym ze stanem tabeli i filtrów strony.

To rozdzielenie sprawia również, że zachowanie asynchroniczne staje się znacznie bezpieczniejsze. Okno edycji może anulować lub odrzucić przestarzałe żądania w momencie zamknięcia. Działanie zapisu może samodzielnie określić, czy okno pozostanie otwarte mimo błędu. Strona nie musi już koordynować wewnętrznych mechanizmów formularza, których tak naprawdę nie rozumie.

Strona nadal kontroluje połączenie pomiędzy tabelą a oknem dialogowym — wie, który rekord jest wybrany i co powinno się stać po pomyślnym edycji. Jednak nie kontroluje już każdego pola i przejścia tylko dlatego, że okno dialogowe zostało uruchomione z tej strony.

Modalne okno może wyglądać tak, jakby znajdowało się warstwowo na wierzchu ekranu, ale ta warstwowość nie oznacza, że cały jego cykl życia stanu musi być przechowywany w komponencie ekranu.

6. Wyodrębnianie sprawdzeń uprawnień z rozproszonego JSX

Logika autoryzacji ma tendencję do stopniowego wnikania do komponentów. Przycisk zostaje ukryty dla zwykłych użytkowników, pozycja menu jest wyłączana po archiwizacji rekordu, a dana sekcja jest renderowana tylko dla administratorów.

To rodzaje warunków mnożą się, ponieważ JSX ułatwia niezwykle dodawanie kolejnych sprawdzeń:

{user.role === "admin" && record.status !== "archived" && (
  <DeleteButton />
)}

Gdy komponent stanie się duży, może zawierać kilka nieco różnych wersji tego, co teoretycznie powinno być tą samą zasadą. Jedna warunek określa, czy coś jest widoczne, inny – czy jego obsługa zostanie uruchomiona, a trzeci – czy pozycja w menu jest wyświetlana w kolorze szarym. Z upływem czasu te kopie zaczynają się różnić i przestają być ze sobą spójne.

Ta różnica jest głównie błędem poprawnościowym, ale pogarsza również czytelność. Struktura rozkładu miesza się z zasadami biznesowymi, a każdy, kto czyta ten komponent, musi mentalnie analizować wyrażenia uprawnień rozproszone w całym drzewie, aby zrozumieć, co robi strona.

Zamiast rozproszać surowe sprawdzenia zasad w JSX, można z góry obliczyć wyraźny zestaw możliwości:

const capabilities = getRecordCapabilities({
  user,
  record,
  organization,
});

Stamtąd komponent może po prostu sprawdzić, czy obecny użytkownik ma uprawnienia do edycji, archiwizacji, eksportu lub usunięcia danego rekordu. Nazwy te opisują faktyczne decyzje dotyczące produktu, zamiast ujawniać surowe pola bazy danych lub łańcuchy ról.

Aby było jasne, to nie przenosi implementacji zasad bezpieczeństwa na stronę klienta. Backend pozostaje rzeczywistą granicą uprawnień — nic się w tym nie zmienia. Obiekt umożliwień na stronie frontendu istnieje wyłącznie po to, by zapewnić interfejsowi spójne źródło prawdy, zamiast ponownie wyprowadzać te same zasady w pięciu różnych elementach wizualnych, które mogłyby potajemnie stracić synchronizację.

Poza tym znacznie ułatwia to testowanie samych reguł. Można sprawdzać logikę uprawnień w odniesieniu do różnych ról, konfiguracji własności, stanów oraz ustawień organizacyjnych, bez konieczności renderowania całego drzewa komponentów. Gdy zmienia się podstawowa polityka, otaczający ją komponent zazwyczaj w ogóle nie musi ulegać zmianie.

Duże drzewa JSX stają się kruche, gdy jednocześnie w nich tworzone są na bieżąco reguły biznesowe. Renderowanie powinno polegać głównie na wykorzystywaniu decyzji, które zostały już podjęte, a nie na ich ponownym tworzeniu od zera w każdej gałęzi warunkowej.

7. Odprowadzanie od użycia ładowania na poziomie całej strony oraz flag błędów

Większość dużych komponentów zaczyna się prosto – z jedną wartością isLoading i jednym elementem error. To w porządku, dopóki strona wykonuje tylko jedną czynność. Jednak w miarę rozwoju funkcjonalności te same dwa flagi zaczynają służyć do opisywania kilku niepowiązanych ze sobą operacji jednocześnie.

Strona może ładować swoje początkowe dane, aktualizować tabelę w tle, zapisywać formularz, usuwać wiersze oraz eksportować raport – czasami wszystko to w tej samej sesji. Jeden wspólny flag boolowski nie może wskazać, która z tych operacji jest aktualnie wykonywana. Gdy jedna prośba zostanie zakończona i flaga zmieni się na false, zupełnie inna prośba może nadal być w trakcie działania. Błąd powstały podczas jakiejś modyfikacji przykrywa błąd, który miał opisywać początkowe załadowanie strony. Cała interfejs blokuje się tylko dlatego, że akurat trwa jakaś niepowiązana aktualizacja w tle.

Lepszym podejściem jest zastąpienie tych flag stanu obejmujących całą stronę stanem właściwym dla konkretnej operacji, którą opisują.

Oznacza to, że początkowe zapytanie może być w trakcie ładowania, podczas gdy istniejąca tabela pozostaje w pełni widoczna i interaktywna. Jedna linia może być usuwana, bez konieczności wyłączania wszystkich pozostałych linii na stronie. Edytor może być w trakcie wysyłania danych, podczas gdy filtry strony pozostają użyteczne. Eksport może wyświetlać własny wskaźnik postępu, bez konieczności sugerowania, że cały ekran stał się niedostępny.

W rezultacie mamy więcej indywidualnych wartości stanu, z których każda ma jednoznaczne znaczenie. W kodzie nie musi nic zgadywać, do czego w danym momencie odnosi się ogólny parametr isLoading.

Ta sama logika obowiązuje w przypadku błędów: nie każdy z nich musi trafiać na jedną, główną stronę błędów. Nieobowiązkowy panel z błędem może pokazać własną opcję ponownej próby w jego miejscu. Błąd mutacji może pozostać ograniczony do procesu, który go wywołał. Główna strona staje się niedostępna tylko wtedy, gdy dane niezbędne do jej wyświetlenia nie udało się załadować.

To zapewnia bardziej odporną interfejs oraz model komponentów, który jest łatwiezy do zrozumienia. Stan przestaje być traktowany jako globalny tylko dlatego, że operacja znajduje się wewnątrz dużego komponentu strony.

Duży komponent często sprawia wrażenie niestabilnego, ponieważ kilka różnych procesów jest zmuszonych do dzielenia się jednym sygnałem. Przyznanie każdemu procesowi własnego stanu pozwala interfejsowi uczciwie pokazywać, co dokładnie funkcjonuje, a co nie, w danym momencie.

Cel nigdy nie polegał tylko na mniejszych plikach

Gdy te wzorce zostaną usunięte, wiele komponentów rzeczywiście staje się krótszych — ale to efekt uboczny, a nie rzeczywisty cel.

Prawdziwą korzyścią jest to, że w obrębie jednej granicy renderowania podejmowanych jest mniej niepowiązanych decyzji. Komponenty potomne otrzymują umowy skonfigurowane pod kątem ich własnych obowiązków, a nie stanu całej funkcjonalności. Identyfikacja referencji zapobiega cichemu unieważnianiu ogromnych poddrzew przy każdym renderowaniu. Dane są pobierane zgodnie z tym, co faktycznie jest widoczne na ekranie, a nie poprzez jedno zapytanie o rozmiarze całej strony. Komponenty wielokrotnego użycia przestają gromadzić nowe flagi dla każdej możliwej wariacji, jakiej ktoś kiedykolwiek potrzebował.

Modały przejmują kontrolę nad własnym stanem. Zasady uprawnień przekształcają się w nazwane możliwości zamiast w warunki wplecione bezpośrednio w kod. Stany ładowania i błędów należą do konkretnych operacji, które je wytwarzają.

Część ekranów pozostaje dużych po prostu dlatego, że sama interfejs jest naprawdę obszerna — i to w porządku. Kod staje się łatwiejszy do obsługi nie dlatego, że stał się mniejszy, ale dlatego, że jego rozmiar odzwierciedla rzeczywistą, widoczną strukturę zamiast ukrytej logiki koordynacji.

W tym momencie pytanie, które warto zadać na temat komponentu React, to nie to, ile ma linii kodu. Chodzi o to, ilu niezależnych czynników mogłoby zmusić go do zmiany. Jeśli edycja kodu oznacza konieczność zrozumienia zapytania do bazy danych, stanu eksportowanego, modelu uprawnień oraz każdego modalu na stronie, problemem nie jest formatowanie ani długość pliku. Granice odpowiedzialności po prostu są wyznaczone w niewłaściwym miejscu.

Duże komponenty React pozostają łatwe do zarządzania, gdy tylko przestaną próbować samodzielnie zachowywać się jak cała aplikacja.

Celem nigdy nie było dalsze dzielenie pliku, dopóki każda funkcja nie stanie się mała.

Celem jest upewnienie się, że każda część funkcji zna jedynie te decyzje, za które faktycznie jest odpowiedzialna.

Literatura pokrewna

  • Wydajność Frontendu: Od Niewidzialnych Luk w Przeglądaniu Kodu do Wskaźników Produktu — Dowiedz się, dlaczego samo przejście przeglądania kodu nie wystarcza, które z Core Web Vitals są naprawdę istotne oraz jak mierzyć i naprawiać problemy z wydajnością Reactu w praktyce.
  • Naprawianie Sytuacji Konkurencyjnych – Debuffing Nie Rozwiązuje Problemów w Interfejsach Wyszukiwania — Dowiedz się, dlaczego sam debuffing nie może zapobiec nadpisywaniu aktualnego stanu interfejsu przez przestarzałe odpowiedzi API, i zapoznaj się z czterema praktycznymi rozwiązaniami umożliwiającymi kontrolowanie kolejności zapytań.