Kategoria:React

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:

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 &&:

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:

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!

O autorze

Olaf Sulich

Olaf jest Frontend Developerem, blogerem i nosi rybacki kapelusz 🎩 Pisze o wszystkim co związane z frontendem, ale nie boi się backendu i designów 🦾 Ma głowę pełną pomysłów i nadzieję, że znajdziesz tutaj coś dla siebie!

Dołącz do społeczności!

Bo w programowaniu liczą się ludzie

Wchodzę