Menu

React Fragment: <>...</> a <Fragment> z przykładami

React Fragment grupuje kilka elementów w jedną zwracaną wartość bez dodawania węzła do strony. Poznaj krótką składnię <>...</>, dowiedz się, kiedy potrzebujesz <Fragment key> i dlaczego fragmenty mają znaczenie w tabelach i listach.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

React Fragment pozwala komponentowi zwrócić kilka elementów bez owijania ich w dodatkowy <div>. Napisz <> przed pierwszym elementem i </> po ostatnim, a React umieści elementy bezpośrednio w rodzicu, bez własnego węzła na stronie.

Każdy Greeting zwraca nagłówek i akapit obok siebie. Usuń linie <> i </>, a podgląd pokaże zamiast tego błąd budowania, bo funkcja próbuje teraz zwrócić dwie wartości.

Dlaczego komponent potrzebuje jednego roota

JSX to wygodniejszy sposób zapisu wywołań funkcji. <h2>Hi</h2> kompiluje się mniej więcej do jsx('h2', { children: 'Hi' }), czyli jednego wyrażenia. Instrukcja return może oddać jedną wartość, więc dwa sąsiednie tagi bez niczego wokół to jak napisanie return a b: kompilator zatrzymuje się z komunikatem "Adjacent JSX elements must be wrapped in an enclosing tag".

Zanim pojawiły się fragmenty, rozwiązaniem był opakowujący <div>. To działa, ale div jest prawdziwym elementem. Pojawia się w DOM, bierze udział w układzie CSS, a niektórzy rodzice w ogóle nie akceptują go jako dziecka. Fragment spełnia regułę jednego roota, a potem znika.

Możesz zobaczyć, że nic nie dodaje. Przykład poniżej renderuje te same dwa elementy przez fragment i przez div, a potem liczy bezpośrednie dzieci każdego kontenera.

Konsola wypisuje 2 dla fragmentu, czyli dwa spany, i 1 dla diva, który je owija. W kontenerze flex albo grid ta różnica decyduje, czy spany są układane jako osobne elementy, czy zbite w jeden.

Krótka składnia i komponent Fragment

Fragment można zapisać na dwa sposoby i oba kompilują się do tego samego:

import { Fragment } from 'react';

// Short syntax, no import needed
<>
    <dt>React</dt>
    <dd>A library for building user interfaces</dd>
</>

// Long form
<Fragment>
    <dt>React</dt>
    <dd>A library for building user interfaces</dd>
</Fragment>

Prawie wszędzie używaj <>...</>. Po <Fragment> sięgaj w jednym przypadku: gdy fragment potrzebuje key. Krótka składnia nie przyjmuje atrybutów, więc <key={id}> nie jest poprawnym JSX. W starszym kodzie możesz też zobaczyć <React.Fragment>; to ten sam komponent, dostępny przez domyślny import React.

Fragmenty z kluczem w listach

Gdy renderujesz listę przez map, każdy element potrzebuje key, żeby React mógł go śledzić między renderowaniami (strona o listach i kluczach wyjaśnia dlaczego). Jeśli każda pozycja to grupa elementów zamiast jednego, klucz trafia na fragment, więc potrzebujesz długiej formy.

Klasycznym przypadkiem jest lista opisowa: każdy wpis to <dt> i <dd>, a <dl> powinien zawierać je bezpośrednio.

Kliknij Reverse, a pary zamienią się miejscami jako całe jednostki, bo każda para ma własny klucz. Opakowujący <div> wokół każdej pary też przyjąłby klucz i jest poprawnym HTML wewnątrz dl, ale dodaje do znaczników trzeci poziom, którego fragment nie dodaje.

Fragmenty w tabelach

W tabelach zabłąkane opakowanie szkodzi najbardziej. <tr> oczekuje komórek <td> i <th> jako bezpośrednich dzieci. Jeśli komponent, który zwraca kilka komórek, owinie je w <div>, przeglądarka potraktuje div jako jedną dziwną komórkę, a komórki w środku przestaną się wyrównywać z nagłówkiem.

Wiersz z fragmentem ma cenę pod Price i stan magazynu pod Stock. W wierszu z divem obie komórki są ściśnięte w kolumnie Price, a kolumna Stock jest pusta. W trybie deweloperskim React wypisuje też ostrzeżenie, że <div> nie może być dzieckiem <tr>; podgląd działa jak build produkcyjny, więc pomija to sprawdzenie, a tutaj widzisz zepsuty układ. Przy renderowaniu na serwerze ten sam błąd może też powodować błędy hydracji, bo parser HTML przeglądarki przenosi źle umieszczonego diva.

Ta sama zasada dotyczy <ul> i <ol>, które powinny zawierać elementy <li>, oraz <select>, który powinien zawierać elementy <option>. Zawsze gdy komponent zwraca części, które należą bezpośrednio do takiego rodzica, zwracaj fragment.

Fragmenty czy tablice

Komponent może też zwrócić tablicę elementów, a React wyrenderuje je po kolei jak fragment. Każdy element zwróconej tablicy potrzebuje własnego klucza, tak jak wynik map, bo React traktuje tablicę jako listę:

function Cells() {
    return [<td key="price">$4</td>, <td key="stock">In stock</td>];
}

To działa, ale jest bardziej zagracone: przecinki między elementami, klucz na każdym z nich i nawias, który łatwo przeoczyć, gdy znaczników przybywa. Używaj fragmentu dla stałej grupy elementów, a tablice zostaw tam, gdzie są naturalne, czyli jako wynik mapowania danych.

Czego fragment nie potrafi

Ponieważ fragment nie renderuje żadnego elementu, nie ma gdzie umieścić klasy, stylu, handlera zdarzeń ani refa. <Fragment className="row"> nie robi nic użytecznego, a jedyny prop, który React odczytuje z fragmentu, to key. Gdy musisz ostylować grupę albo nasłuchiwać na niej kliknięć, potrzebujesz jednak prawdziwego elementu i to jest w porządku: wybierz taki, który jest poprawny w danym miejscu, na przykład <tbody> do grupowania wierszy albo <li> na pozycję listy.

Fragment działa wszędzie tam, gdzie element, także w renderowaniu warunkowym: {show && <>...</>} pokazuje albo ukrywa całą grupę naraz.

Najczęściej zadawane pytania

Co to jest Fragment w React?

Fragment to opakowanie, które pozwala komponentowi zwrócić kilka elementów jako jedną wartość bez dodawania węzła DOM. <><h1 /><p /></> umieszcza h1 i p bezpośrednio w rodzicu.

Czym różni się <> od <Fragment>?

Dają ten sam wynik. <>...</> to krótka składnia. <Fragment> to długa forma, którą importujesz z react, i tylko ona może przyjąć key, którego potrzebujesz, gdy renderujesz fragmenty w liście.

Dlaczego używać Fragmentu zamiast diva?

Div to prawdziwy element. Może zepsuć układy, które oczekują bezpośrednich dzieci (kontenery flex i grid, ul, table, tr), i dodaje szum do DOM. Fragment nie dodaje niczego.

Czy Fragment może mieć className albo onClick?

Nie. Fragment nie renderuje żadnego elementu, więc nie ma na czym umieścić klasy, stylu ani handlera zdarzeń. Jedyny prop, który przyjmuje, to key. Jeśli potrzebujesz któregoś z nich, użyj prawdziwego elementu.

Dlaczego dostaję błąd "Adjacent JSX elements must be wrapped in an enclosing tag"?

Komponent zwrócił dwa sąsiednie elementy bez niczego wokół nich. JSX kompiluje się do jednego wyrażenia, więc owiń te elementy w <>...</>.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ