Pułapki konstrukcji warunkowych w React i JSX
- Czas potrzebny na przeczytanie:5 minut
- Opublikowane:
Warunkowe renderowanie w React, w przeciwieństwie do takiego Vue.js może odbywać się za pomocą operatorów logicznych. Takie rozwiązanie to w teorii łatwiejsza do nauczenia składania, z drugiej strony niesie ono za sobą szereg pułapek, na które warto zwracać uwagę.
Strzeż się zera
Bardzo często zdarza nam się korzystać z operatora AND (&&
), żeby wyświetlić jakiś kawałek JSX. Załóżmy, że tworzymy komponent odpowiadający za wyświetlanie ceny i ilości danego przedmiotu w koszyku. Ilość jest niezbędna do wyświetlenia całego komponentu, a przez to, że pochodzi z zewnętrznego serwisu może mieć wartość typu number
lub być pod postacią undefined
:
quantity && <PriceAndQuantity quantity={quantity} />;
Jeśli ilość będzie undefined
, to po prostu nie wyświelimy komponentu. A co jeśli jej wartość będzie 0
? Spodziewalibyśmy się, że tak jak w przypadku undefined
komponent nie zostanie wyrenderowany, w końcu 0
jest falsy, dzieje się jednak inaczej... Do naszego drzewa DOM przecieka faktyczne 0
! Ale jak?
Właśnie z powodu tego, że 0
jest wartością falsy, JavaScript przetworzy je i natychmiastowo zwróci. Kilka przykładów wykorzystania &&
:
0 && true; // 0
true && 0; // 0
false && true; // false
true && ''; // ''
Dlaczego więc takie false
albo undefined
nie jest renderowane przez JSX w DOM? Ponieważ 0
, w przeciwieństwie do tych wartości, jest w 100% poprawnym React Node, dlatego możemy zrobić <p>ilość sztuk: {quantity}</p>
i dostaniemy poprawną wiadomość z ilością produktów.
Bardzo częstym przykładem jest również sprawdzanie długości tablicy i na bazie tego renderowane komponentu:
Jak to naprawić? Mam kilka asów w rękawie:
Pierwszeństwo operatorów
Budujemy dashboard dla naszego serwera Discord, w którym administratorzy i moderatorzy będą mogli zmieniać ustawienia serwera. Każdy użytkownik w naszej aplikacji będzie miał specjalne pole roli, na bazie której będziemy chcieli renderować komponent ustawień serwera:
// prettier-ignore
user.role === 'admin' || user.role === 'moderator' && <ServerSettings />;
Olaf, szukam i szukam tej pułapki, ale w tym kodzie nie ma nic nadzwyczajnego!
Wszystko rozchodzi się o tzw. pierwszeństwo operatorów. Logiczny operator AND (&&
) ma pierwszeństwo nad OR (||
), dlatego pod spodem nasz kodem będzie wyglądał w ten sposób:
Jak już możesz się domyślić, ten kod skończyłby się istną katastrofą! Gdy nasz użytkownik będzie miał rolę administratora, wyrażenie user.role === 'admin'
zamieni się na true
a to przy operatorze OR (||
) zostanie zwrócone do JSX...
Sposób na poprawienie tego, to odpowiednie dostosowanie nawiasów:
Newsletter dla Frontend Developerów 📮
Czytelność
Ternaries i czytelność to często toksyczny związek ❌ To jedna z najczęstszych i najbardziej przebiegłych pułapek!
Wróćmy do dashboardu, dla każdej roli użytkownika chcemy wyświetlać osobny dashboard:
Ałć! Z każdą dodaną rolą ten kod będzie wyglądał tylko gorzej. Spróbujmy podejścia z klasycznymi ifami:
Lepiej lepiej, ale nadal te ify mnie nie przekonują, co powiesz na switcha?
Hmm, sam nie wiem, czy mamy coś jeszcze w zanadrzu?
React-If
Jakiś czas temu natrafiłem na dość ciekawą bilbiotekę, react-if zamiast korzystać z ternatires, używa zwykłych komponentów:
const Dashboard = ({ user }) => (
<div>
<Header />
<If condition={user.role === 'admin'}>
<Then>
<AdminUserDashBoard />
</Then>
</If>
<If condition={user.role === 'moderator'}>
<Then>
<ModeratorUserDashBoard />
</Then>
</If>
<If condition={user.role === 'normal'}>
<Then>
<NormalUserDashBoard />
</Then>
</If>
<Unless condition={['admin', 'moderator', 'normal'].includes(user.role)}>
<span>Twoja rola nie jest obecnie wspierana</span>
</Unless>
<Footer />
</div>
);
Mamy tutaj do dyspozycji takie komponetny jak <If/>
, <Else/>
, <When/>
, <Unless/>
, a nawet odpowiednik klasycznego <Switch/>
:
Frontend Developerzy, którzy przyzwyczajeni są do pisania we Vue, czy Angularze znają podobne patterny, ale mi jakoś ciężko się do nich przyzwyczaić, chociaż muszę przyznać, że to o wiele bardziej czytelne :)
Pattern Matching
A co powiesz na Pattern Matching? Halo Olaf, ale przecież ECMA proposal dla tej konstrukcji jest dopiero w pierwszym stage'u! Uwaga, mam godnego zastępce, ts-pattern
to bilbioteka do pseudo Pattern Matchingu zbudowana z myślą o TypeScripcie 💙
Wygląda to naprawdę świetnie i jest type-safe! Taką konstrukcję możemy również wynieść do funkcji renderUserDashboard
:) Czego chcieć więcej?
Minusem takiego rozwiązania jest na pewno konieczność instalowania dodatkowej biliboteki, no cóż, coś za coś.
Podsumowanie
It's a trap! Tak można podsumować konstrukcje warunkowe w React i JSX. Uważajcie na siebie i nie dajcie się złapać!
Do usłyszenia!