Żeby wyrenderować listę w React, wywołaj map() na tablicy i zwróć fragment JSX dla każdego elementu. Najbardziej zewnętrznemu elementowi każdej pozycji nadaj prop key z wartością, która tę pozycję identyfikuje, zwykle jej ID, żeby React mógł odróżnić elementy, gdy lista się zmienia.
Dodaj { id: 'dat', name: 'Date', price: 4 } do tablicy, a pojawi się czwarta linia. JSX nie ma własnej składni pętli: map zwraca tablicę elementów, a React renderuje tablicę, renderując po kolei każdy element.
Renderowanie tablic przez map
map wywołuje twoją funkcję raz dla każdego elementu i zbiera wyniki w nową tablicę. Wewnątrz nawiasów klamrowych JSX ta tablica to po prostu wartość, więc możesz mapować bezpośrednio w znacznikach, jak powyżej, albo najpierw zbudować tablicę:
const items = fruits.map((fruit) => <li key={fruit.id}>{fruit.name}</li>);
return <ul>{items}</ul>;
Dwa szczegóły często sprawiają kłopot. Jeśli w funkcji strzałkowej używasz nawiasów klamrowych, potrzebujesz return: fruits.map((f) => { return <li key={f.id}>{f.name}</li>; }). Bez niego funkcja zwraca undefined, a lista jest pusta. Poza tym key umieszcza się na elemencie zwracanym z map, a nie na elemencie w jego środku. Jeśli pozycja jest komponentem, umieść klucz na komponencie: <FruitRow key={fruit.id} fruit={fruit} />.
Najpierw filter, potem map
Żeby pokazać część elementów, najpierw przefiltruj tablicę, a potem zmapuj wynik. Każda metoda wykonuje jedno zadanie, a łańcuch czyta się jak zdanie „owoce, które pasują, jako elementy listy".
Wpisz er w polu wyszukiwania, a lista zawęzi się do Eraser, Ruler i Stapler; zaznacz In stock only, a zniknie też Ruler. Przefiltrowana lista jest wyliczana podczas renderowania z dwóch kawałków stanu, więc w stanie nie ma drugiej tablicy, którą trzeba synchronizować. visible.length === 0 && to bezpieczne użycie &&, bo porównanie daje prawdziwą wartość logiczną (strona o renderowaniu warunkowym pokazuje, co się dzieje z samym 0).
Do czego służą klucze
Gdy lista renderuje się ponownie, React porównuje nową tablicę elementów z poprzednią. Klucze pozwalają je dopasować: element z kluczem 3 teraz to element z kluczem 3 wcześniej, nawet jeśli się przesunął. React zachowuje węzeł DOM tej pozycji i stan jej komponentu, a przesuwa albo aktualizuje tylko to, co się zmieniło. Elementy z nowymi kluczami są tworzone, a te, których klucze zniknęły, są usuwane.
Bez kluczy React może dopasowywać tylko po pozycji. To jest w porządku, dopóki nic się nie przesuwa, i błędne, gdy tylko coś się przesunie.
Dlaczego klucze z indeksu się psują
map przekazuje indeks jako drugi argument, więc key={index} kusi. Indeks opisuje pozycję, a nie element. Wstaw element na górę, a indeks każdej pozycji przesunie się o jeden, więc React uzna, że element 0 to wciąż element 0, i zostawi tam jego stary stan.
Demo poniżej renderuje tę samą listę dwa razy, raz z kluczem z indeksu, a raz z ID. Każdy wiersz ma pole, które trzyma własny tekst.
W obu listach wpisz math obok Ady, a potem kliknij Add to top. Na liście z ID notatka zostaje przy Adzie. Na liście z indeksem notatka przeskakuje do nowej osoby, bo pierwsze pole to wciąż pole na pozycji 0, a na pozycji 0 jest teraz ktoś inny. Imiona są poprawne na obu listach, bo pochodzą z danych; źle zachowuje się tylko stan, który żyje w DOM i w komponentach. Prawdziwe aplikacje trafiają na to przy polach wyboru, fokusie, animacjach i każdym komponencie, który trzyma useState.
Klucze z indeksu są bezpieczne tylko wtedy, gdy spełnione są wszystkie warunki: lista nigdy nie jest sortowana ani filtrowana, elementy nigdy nie są wstawiane ani usuwane inaczej niż na końcu, a elementy nie mają własnego stanu. Statyczna lista linków w stopce się kwalifikuje. Coś, co użytkownik może edytować, zwykle nie.
Skąd brać klucze
Najlepszy klucz to ID, które już jest w danych: ID z bazy danych, SKU produktu, slug, nazwa użytkownika. Dane z serwera prawie zawsze je mają.
Elementom tworzonym w przeglądarce nadaj ID w chwili utworzenia i przechowuj je razem z elementem: licznik, jak w demo powyżej, albo crypto.randomUUID(). Nigdy nie generuj klucza podczas renderowania:
// Wrong: a new key on every render, so React remounts every item every time
{todos.map((todo) => <Todo key={Math.random()} todo={todo} />)}
// Right: the ID is created once, in the event that adds the item
function addTodo(text) {
setTodos([...todos, { id: crypto.randomUUID(), text }]);
}
Klucz, który zmienia się przy każdym renderowaniu, jest gorszy niż indeks: każdy element jest za każdym razem niszczony i tworzony od nowa, pola tracą fokus w trakcie pisania i nic nie zachowuje stanu. Tę samą sztuczkę można wykorzystać celowo: zmiana klucza komponentu go resetuje, czego strona o useState używa do czyszczenia formularza.
Klucze są unikalne tylko wśród rodzeństwa
Klucz musi być unikalny w obrębie jednej listy, a nie w całej aplikacji. Dwie osobne listy mogą używać tych samych ID, bo React porównuje klucze tylko wśród dzieci tego samego rodzica.
Obie sekcje mają dania z kluczami 1 i 2 i to jest poprawne: każdy ul to osobna lista. Problemem są zduplikowane klucze w jednej liście: React nie potrafi odróżnić kolidujących elementów i przy aktualizacji listy może je zduplikować albo zgubić.
Gdy jedna pozycja renderuje kilka sąsiednich elementów, na przykład <dt> i <dd>, owiń je w <Fragment key={item.id}>. Krótka składnia <> nie przyjmuje klucza.
Ostrzeżenie o „unique key prop"
Jeśli pominiesz klucz, React wypisze w konsoli przeglądarki w trybie deweloperskim:
Each child in a list should have a unique "key" prop.
Check the render method of `App`.
Druga linia podaje komponent, którego renderowanie utworzyło listę, co mówi ci, które map trzeba poprawić. Dwa elementy rodzeństwa z tym samym kluczem dają inny komunikat, zaczynający się od "Encountered two children with the same key". Podgląd na tej stronie działa jak build produkcyjny, który pomija to sprawdzenie, więc nie zobaczysz tu ostrzeżenia, nawet jeśli usuniesz key. Mimo to usuń jeden w pierwszym przykładzie: lista nadal się renderuje, bo brakujący klucz to ryzyko błędnego działania, a nie awaria. Żeby naprawić ostrzeżenie, dodaj stały klucz do elementu zwracanego z map; dodanie key={index} je ucisza, ale przywraca błąd pokazany powyżej.
Pamiętaj też, że key nie jest przekazywany do twojego komponentu. Wewnątrz FruitRow props.key ma wartość undefined. Jeśli komponent potrzebuje ID, przekaż je drugi raz jako zwykły prop: <FruitRow key={f.id} id={f.id} />.
Najczęściej zadawane pytania
Dlaczego React potrzebuje klucza na elementach listy?
Klucze mówią Reactowi, który element jest którym między renderowaniami. Gdy tablica się zmienia, React dopasowuje stare i nowe elementy po kluczu, więc może zachować stan i węzeł DOM każdego elementu razem z właściwymi danymi, zamiast zgadywać po pozycji.
Czy mogę użyć indeksu tablicy jako klucza?
Tylko dla listy, która nigdy nie zmienia kolejności i do której nigdy nie wstawia się ani nie usuwa elementów w środku. Jeśli elementy mogą się przesuwać, klucz z indeksu wiąże stan z pozycją, więc wpisany tekst, pola wyboru i fokus lądują na złym elemencie.
Czego używać jako klucza?
Identyfikatora, który należy do danych: ID z bazy danych, SKU produktu, nazwy użytkownika. Dla elementów tworzonych w przeglądarce wygeneruj ID raz, przy tworzeniu elementu (licznik albo crypto.randomUUID()), nigdy podczas renderowania.
Czy klucze muszą być unikalne w całej aplikacji?
Nie. Klucze muszą być unikalne tylko wśród rodzeństwa w tej samej liście. Dwie różne listy mogą używać tych samych kluczy.
Czy komponent może odczytać własny klucz?
Nie. key jest używany przez React i nie jest przekazywany do komponentu jako prop. Jeśli komponent potrzebuje ID, przekaż je ponownie pod inną nazwą, na przykład id={item.id}.
Jak naprawić "Each child in a list should have a unique key prop"?
Dodaj key do najbardziej zewnętrznego elementu, który zwracasz z map, używając stałego ID elementu. Jeśli tym elementem jest fragment, użyj <Fragment key={id}>, bo <> nie przyjmuje klucza.