Klucze w React
Jednym z najpopularniejszych błędów w trakcie pracy z React jest błąd dotyczący nieprzypisania atrybutu key do elementów listy. Wbrew pozorom nie jest to taka błaha sprawa. Klucze w React są mocno powiązane ze sposobem w jaki React przebudowuje DOM - można powiedzieć, że wręcz ich potrzebuje, żeby zrobić to możliwie najoptymalniej.

Z tego artykułu dowiesz się:
Jak działa proces rekoncyliacji w React?
Kiedy i które elementy listy przebuduje React?
Jaki powinien być idealny klucz?
Każdy, kto pracował z React przynajmniej przez chwilę, na pewno zauważył ciekawy błąd, pojawiający się przy okazji mapowania tablicy z danymi do listy elementów. Zapewne także wielu z Was wie, jak go naprawić.
Wystarczy przecież podać key
prop do każdego kolejnego elementu tworzonej listy. Czy zastanawialiście się dlaczego twórcy React tak bardzo zwracają uwagę na ten z pozoru nieistotny element? Poniższy przykład ilustruje powyższą sytuację.
Co to jest key?
key
jest specjalną właściwością, która przyjmuje wartość w typie string (ciąg znaków). Zgodnie z dokumentacją klucze są potrzebne, aby React mógł poprawnie identyfikować poszczególne elementy i oceniać, co powinien z nimi zrobić - dodać, usunąć czy może zaktualizować. Co istotne, brak użycia klucza albo niepoprawne jego wykorzystanie może powodować problemy z wydajnością, poprzez tworzenie zbędnych re-renderowań komponentów.
Rekoncyliacja
Aby zrozumieć dlaczego tak się dzieje, warto zwrócić uwagę na algorytm różnicujący, za pomocą którego React decyduje, które elementy należy zmienić w aplikacji. W dużym uproszczeniu, algorytm ten działa tak, że porównuje dwa drzewa (struktury) naszej aplikacji - dotychczasową oraz tę, która ma zostać zaimplementowana. W pierwszej kolejności React porównuje elementy nadrzędne drzewa i na tej podstawie podejmuje decyzje, co należy zrobić. Przykładowo, jeżeli elementy nadrzędne są różnego typu: <div>
i <section>
, całe drzewo od tego miejsca w dół zostanie usunięte i wyrenderowane ponownie.
Pomimo, że elementy podrzędne (dzieci) będą identyczne, to algorytm podejmie decyzję o ich usunięciu i ponownym wyrenderowaniu całości, ponieważ ich rodzic zmienił typ.
Case listy
Wróćmy jednak do naszego problemu listy. Posiadając listę elementów i aktualizując ją poprzez dodanie do niej kolejnego elementu (przy założeniu, że typ rodzica się nie zmienił) - React przeiteruje się poprzez kolejne elementy listy i je ze sobą porówna: pierwszy z pierwszym, drugi z drugi itd. Jeśli napotka różnicę lub nowy element, podejmie decyzję, aby zaktualizować odpowiedni fragment. Prosta sprawa o ile kolejne elementy są identyczne albo nowy element wyląduje na końcu listy.
Trudniej będzie, gdy postanowimy dodać element na początek naszej listy. Wówczas rekurencja po kolejnych elementach zawsze wymusi ponowne wyrenderowanie całej listy, bo pierwszy element ze starej struktury będzie na drugiej pozycji w nowej strukturze.
React rozwiązuje ten problem poprzez atrybut key
, który pomaga mu identyfikować poszczególne elementy listy, aby wyeliminować niepotrzebne zmiany w drzewie.
Jaki powinien być klucz?
Tutaj odpowiedź wydaje się prosta. Klucz powinien być unikalny, przynajmniej na poziomie elementów, będących rodzeństwem, np. kolejne elementy listy. Biorąc pod uwagę, że algorytm różnicujący na początku bierze pod uwagę rodzica, jego typ i właściwości, klucz nie jest aż tak istotny.
Klucz powinien być też stabilny - co oznacza, że pomiędzy poszczególnymi re-renderowaniami nie powinien się zmieniać. Zmiana klucza spowoduje, że React podejmie decyzję o usunięciu danego komponentu i jego ponownym przygotowaniu.
Index tablicy jako klucz?
To chyba jeden z częściej spotykanych pomysłów na usuwanie nieznośnego błędu z console’i, o którym wspominałem na początku. Czy zatem można tak robić?
Nie jest to zalecane, właśnie dlatego, że index nie jest przypisany do konkretnej wartości w tablicy, a do pozycji w tablicy. Czyli jeśli usuniemy lub dodamy element w środku lub na początku naszej tablicy to kolejne jej elementy otrzymają nowy index, a zatem taki klucz nie będzie już stabilny. Stracimy kontrolę nad tym, które elementy powinny się renderować ponownie.
Idealny klucz
Idealnym kluczem w większości przypadków, będzie ID, które otrzymujemy z backendu. Takie ID najprawdopodobniej będzie trzymane w bazie danych, więc nie będzie podlegało nieoczekiwanym zmianom. Taki klucz będzie stabilny i unikalny, a zatem wpłynie pozytywnie na optymalizację naszej aplikacji.
Jeśli natomiast nie mamy takiego identyfikatora to zastanówmy się czy któraś wartość w obiektach, po których się iterujemy, nie jest wystarczajaco unikalna - być może jest to po prostu name
?
Podsumowanie
Aplikacje w pewnym momencie swojego życia dochodzą do punktu, w którym należy powiedzieć stop i rozpocząć wdrażanie optymalizacji oraz usprawnień w kodzie. W moim odczuciu tak proste optymalizacje, jak klucz nadawany elementom listy, nie powinny czekać do tego momentu. Poprawne wdrażanie ich w życie nie kosztuje nas dużo, a przedłuża żywotność naszej aplikacji o kolejne tygodnie.
Klucz powinien być unikalny i stabilny pomiędzy re-renderowaniami. Nie korzystajmy z indeksów tablicy, chyba że robimy to z pełną świadomością. Wszak wiedza o tym, jakie zagrożenia niesie ze sobą wykorzystanie złych wartości jako kluczy, wykorzystana w odpowiedni sposób, może stać się naszym sprzymierzeńcem.
Komentarze (0)
Jeszcze nikt nic nie napisał, ale to znaczy że... możesz być pierwszy/pierwsza.