Strona główna / Artykuły / Fragmenty React i StrictMode: lżejszy kod markup oraz szybsze wykrywanie błędów

Fragmenty React i StrictMode: lżejszy kod markup oraz szybsze wykrywanie błędów

Dowiedz się, w jaki sposób fragmenty React grupują elementy bez dodatkowych otoczeń DOM oraz jak podwójne uruchamianie StrictMode przeznaczone wyłącznie do rozwoju ujawnia nieczyste renderowania i efekty.

976 słów

Dwie funkcje React, które jak dotąd nie rysują nic na ekranie, wpływają niemal na każdą strukturę komponentów: Fragmentsy i StrictMode. Fragmentsy chronią DOM przed elementami otaczającymi, które istnieją wyłącznie po to, by zadowolić wymagania JSX, natomiast StrictMode celowo obciąża komponenty podczas rozwoju, aby błędna logika ujawniła się przed użytkownikami.

Dlaczego JSX potrzebuje pojedynczego korzenia

JSX kompiluje każdy tag na wywołanie funkcji, więc dwa tagi siostrzane zwrócone razem stanowią dwie wartości, podczas gdy oczekuje się tylko jednej. To powoduje błąd kompilacji:

return (
  <h1>Hello</h1>
  <p>Welcome</p>
);

Tradycyjnym rozwiązaniem było umieszczenie tych tagów siostrzanych wewnątrz div:

return (
  <div>
    <h1>Hello</h1>
    <p>Welcome</p>
  </div>
);

Dodatkowy węzeł ma swoje konsekwencje: pogłębia strukturę DOM, zakłóca układy typu flex lub grid, które wymagają bezpośrednich dzieci, komplikuje selektory i może prowadzić do nieważnego lub mniej dostępnego HTML.

Grupowanie bez elementu otaczającego

A Fragment grupuje dzieci w React bez tworzenia żadnego elementu DOM. Krótka składnia polega na pustej parze tagów:

return (
  <>
    <h1>Hello</h1>
    <p>Welcome</p>
  </>
);

Forma wyraźna, React.Fragment, robi to samo i jest wymagana, gdy trzeba przekazać właściwość:

return (
  <React.Fragment>
    <h1>Hello</h1>
    <p>Welcome</p>
  </React.Fragment>
);

Wyrenderowany HTML zawiera tylko h1 i p. Zysk wydajności jest niewielki; prawdziwą korzyścią jest poprawna struktura markupu.

Zwracanie elementów rodzeństwa

Komponent, który tworzy kilka elementów tego samego typu, może pozostawić rozplanowanie ich układu rodzicowi:

function Card() {
  return (
    <>
      <h2>Title</h2>
      <p>Description</p>
    </>
  );
}

Fragmenty z kluczem w listach

Gdy przetwarzamy dane, w których każdy element generuje więcej niż jeden element, React nadal wymaga stabilnego key dla każdego z nich. Krótka składnia <> nie obsługuje atrybutów, więc należy użyć React.Fragment z kluczem:

items.map(item => (
  <React.Fragment key={item.id}>
    <h2>{item.title}</h2>
    <p>{item.description}</p>
  </React.Fragment>
));

Kolumny i wiersze tabeli

Tabele HTML mają ścisły model zawartości: element tr może zawierać jedynie elementy td lub th. Użycie otaczającego go elementu div jest w takim przypadku nieważne. Fragment umożliwia komponentowi dostarczenie kilku komórek do wiersza należącego do jego rodzica:

function Row() {
  return (
    <>
      <td>A</td>
      <td>B</td>
    </>
  );
}

Co sprawdza StrictMode

StrictMode nie renderuje nic, nie dodaje żadnych elementów i nie ma wpływu na wersje produkcyjne. W środowisku rozwojowym umożliwia dodatkowe sprawdzenia, między innymi:

  • ostrzeżenia dotyczące metod cyklu życia klas starszych, uznawanych za niebezpieczne, takich jak componentWillMount
  • ostrzeżenia dotyczące przestarzałych interfejsów API, takich jak referencje do ciągów znaków i findDOMNode
  • dwukrotne wywoływanie ciał komponentów, funkcji inicjalizacyjnych i aktualizujących w celu wykrycia nieczystego renderowania
  • uruchamianie efektów poprzez dodatkowy cykl konfiguracji, czyszczenia i ponownej konfiguracji podczas montowania w celu wykrycia braku czyszczenia

W zależności od wersji React lista ta ulega zmianom, dlatego sprawdź aktualną dokumentację dla swojej wersji.

Celowane podwójne renderowanie

Rozważ komponent, który loguje proces renderowania:

function App() {
  console.log("Rendered!");
  return <h1>Hello</h1>;
}

W trybie StrictMode podczas rozwoju konsola wyświetla tę wiadomość dwa razy:

Rendered!
Rendered!

To jest elementem projektu. Funkcja renderująca powinna być czysta: przy tych samych propach i stanie zwraca ten sam wynik i nie zmienia nic poza sobą. Wywołanie jej dwukrotnie sprawia, że naruszenia, takie jak modyfikacja wspólnej zmiennej, prowadzą do wyraźnie błędnych rezultatów. Czystość jest ważna, ponieważ renderowanie może być równoczesne, zawieszone, odrzucone lub powtórzone, a kod zakładający dokładnie jedno renderowanie na aktualizację przestaje działać w takich warunkach.

Włączanie tego trybu

Otocz drzewo, które chcesz sprawdzić – zazwyczaj całą aplikację – u jego korzenia:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Efekty w StrictMode: tablica zależności to nie rozwiązanie

Oto efekt bez tablicy zależności. Uruchamia się po każdym renderowaniu, więc każda aktualizacja wywołuje kolejną prośbę:

useEffect(() => {
  console.log("Fetching...");
  fetch("/api");
});

Dodanie pustej tablicy ogranicza uruchamianie efektu tylko do momentu montowania komponentu:

useEffect(() => {
  console.log("Fetching...");
  fetch("/api");
}, []); // stable dependency

To zmiana jest poprawna, ale nie zapobiega podwójnej prośbie w środowisku rozwojowym. Od React 18 StrictMode montuje komponent, uruchamia jego funkcję czyszczenia, a następnie montuje go ponownie, więc efekt z [] nadal jest uruchamiany dwa razy. Podwójne uruchomienie to sygnał, a nie błąd. Prawdziwym rozwiązaniem jest funkcja czyszczenia, która sprawia, że drugie uruchomienie staje się bezpieczne – zazwyczaj poprzez przerwanie pierwszej prośby za pomocą AbortController lub zignorowanie jej wyniku przy użyciu flagi. Taki efekt jest również bezpieczny w przypadku rzeczywistego ponownego montowania w środowisku produkcyjnym.

Porównanie

  • Cel: Fragments grupują elementy; StrictMode ujawnia błędy.
  • Wynik w DOM: żaden z nich nie dodaje elementu.
  • Wpływ na produkcję: żaden dla obu.
  • Zachowanie w czasie wykonywania: Fragments są renderowane normalnie; StrictMode powoduje podwójne wywołanie renderowania i efektów tylko w fazie rozwoju.
  • Korzyści: czystszy, poprawny markup w porównaniu z przewidywalnym kodem wolnym od efektów ubocznych.

Minimalne ustawienie do przetestowania

Poniższy komponent zwraca dwa elementy siostrzane za pośrednictwem Fragmentu:

export default function App() {
  return (
    <>
      <h1>Hello World</h1>
      <p>Rendered using Fragments</p>
    </>
  );
}

Plik wejściowy renderuje go wewnątrz StrictMode, więc każda nieczysta logika dodana później jest natychmiast wykrywana podczas rozwoju:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Główne wnioski

  • Używaj Fragmentu zawsze wtedy, gdy element otaczający istniałby tylko po to, by spełnić wymagania JSX, a w listach stosuj React.Fragment z atrybutem key.
  • Zachowaj StrictMode włączone podczas rozwoju; podwójne logowanie oraz efekty działania skryptów są celowymi narzędziami diagnostycznymi.
  • Popraw problem podwójnego uruchamiania efektów poprzez prawidłowe usuwanie zasobów, a nie poprzez ingerencję w zależności.
  • Literatura pokrewna