Uslovno renderovanje

Vaše komponente često trebaju prikazivati različite stvari u zavisnosti od različitih uslova. U React-u, možete uslovno renderovati JSX upotrebom JavaScript sintakse poput if iskaza, && i ? : operatora.

Naučićete:

  • Kako da vratite različit JSX u zavisnosti od uslova
  • Kako da uslovno uključite ili isključite deo JSX-a
  • Uobičajene prečice za uslovnu sintaksu koje ćete sresti u React projektima

Uslovno vraćanje JSX-a

Napravimo PackingList komponentu koja će renderovati par Item-a, koji mogu, a ne moraju biti spakovani:

function Item({ name, isPacked }) {
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Lista za pakovanje od Sally Ride</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Svemirsko odelo" 
        />
        <Item 
          isPacked={true} 
          name="Kaciga sa zlatnim listom" 
        />
        <Item 
          isPacked={false} 
          name="Fotografija od Tam" 
        />
      </ul>
    </section>
  );
}

Primetite da neke Item komponente imaju isPacked prop setovan na true umesto na false. Želimo dodati kvačicu (✅) za spakovane proizvode ako je isPacked={true}.

Ovo možete napisati kao if/else izraz poput:

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

Ako je isPacked prop true, ovaj kod vraća drugačije JSX stablo. Sa ovom promenom, neki proizvodi će imati kvačicu na kraju:

function Item({ name, isPacked }) {
  if (isPacked) {
    return <li className="item">{name}</li>;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Lista za pakovanje od Sally Ride</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Svemirsko odelo" 
        />
        <Item 
          isPacked={true} 
          name="Kaciga sa zlatnim listom" 
        />
        <Item 
          isPacked={false} 
          name="Fotografija od Tam" 
        />
      </ul>
    </section>
  );
}

Promenite šta se vraća u oba slučaja i vidite kako se rezultat menja!

Uočite da se kreira logika grananja sa JavaScript-ovim if i return iskazima. U React-u, JavaScript je zadužen za kontrolni tok (npr. uslove).

Uslovno renderovanje ničega sa null

U nekim situacijama, nećete želeti da renderujete ništa. Na primer, uopšte ne želite da prikažete spakovane proizvode. Komponenta mora nešto da vrati. U ovom slučaju, možete vratiti null:

if (isPacked) {
return null;
}
return <li className="item">{name}</li>;

Ako je isPacked true, komponenta će vratiti ništa, null. U suprotnom, vratiće JSX koji se renderuje.

function Item({ name, isPacked }) {
  if (isPacked) {
    return null;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Lista za pakovanje od Sally Ride</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Svemirsko odelo" 
        />
        <Item 
          isPacked={true} 
          name="Kaciga sa zlatnim listom" 
        />
        <Item 
          isPacked={false} 
          name="Fotografija od Tam" 
        />
      </ul>
    </section>
  );
}

U praksi, vraćanje null iz komponente nije uobičajeno jer može iznenaditi developera koji pokuša da ga renderuje. Češće ćete uslovno uključiti ili isključiti komponentu u JSX-u roditeljske komponente. Evo kako to možete uraditi!

Uslovno uključivanje JSX-a

U prethodnom primeru, kontrolisali ste koje (možda nijedno!) JSX stablo će komponenta vratiti. Možda ste već uočili neko ponavljanje:

<li className="item">{name}</li>

je veoma slično sa

<li className="item">{name}</li>

Obe uslovne grane vraćaju <li className="item">...</li>:

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

Iako ovo ponavljanje nije štetno, ipak čini vaš kod težim za održavanje. Šta ako želite promeniti className? Morali biste to učiniti na dva mesta u kodu! U ovakvim situacijama možete uslovno uključiti malo JSX-a da bi vaš kod bolje ispoštovao DRY.

Uslovni (ternarni) operator (? :)

JavaScript sadrži kompaktnu sintaksu za pisanje uslovnih izraza — uslovni operator ili “ternarni operator”.

Umesto ovoga:

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

Možete napisati ovo:

return (
<li className="item">
{isPacked ? name + ' ✅' : name}
</li>
);

Ovo možete pročitati kao “ako je isPacked true, onda (?) renderuj name + ' ✅', u suprotnom (:) renderuj name.

Deep Dive

Da li su ovi primeri potpuno jednaki?

Ako dolazite sa pozadinom objektno-orijentisanog programiranja, mogli biste pretpostaviti da se dva primera gore suptilno razlikuju zato što jedan od njih može kreirati dve “instance” <li>. Ali, JSX elementi nisu “instance” zato što ne drže stanje i nisu pravi DOM čvorovi. Oni si laki opisi, nalik na nacrte. Tako da ova dva primera, u suštini, jesu potpuno jednaki. Očuvanje i resetovanje state-a detaljno opisuje kako ovo radi.

Hajde sad da obmotamo tekst spakovanog proizvoda u novi tag, <del>, kako bi ga precrtali. Možete dodati i nove linije i zagrade kako bi bilo lakše da ugnjezdite dodatni JSX u oba slučaja:

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {isPacked ? (
        <del>
          {name + ' ✅'}
        </del>
      ) : (
        name
      )}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Lista za pakovanje od Sally Ride</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Svemirsko odelo" 
        />
        <Item 
          isPacked={true} 
          name="Kaciga sa zlatnim listom" 
        />
        <Item 
          isPacked={false} 
          name="Fotografija od Tam" 
        />
      </ul>
    </section>
  );
}

Ovaj stil radi dobro za jednostavne uslove, ali koristite ga umereno. Ako vaše komponente postanu neuredne zbog previše ugnježdenih uslovnih markup-a, razmotrite izdvajanje dečjih komponenata kako biste ih poboljšali. U React-u, markup je deo vašeg koda, tako da možete koristiti alate poput promenljivih i funkcija da biste pojednostavili kompleksne izraze.

Logički “I” operator (&&)

Još jedna uobičajena prečica koju ćete sresti je JavaScript-ov logički “I” (&&) operator. Često se koristi unutar React komponenata kada želite da renderujete neki JSX kad je uslov true, ili da ne renderujete ništa u suprotnom. Sa && možete uslovno renderovati kvačicu samo kad je isPacked true:

return (
<li className="item">
{name} {isPacked && '✅'}
</li>
);

Ovo možete pročitati kao “ako je isPacked, onda (&&) renderuj kvačicu, u suprotnom ne renderuj ništa”.

Evo primera:

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✅'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Lista za pakovanje od Sally Ride</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Svemirsko odelo" 
        />
        <Item 
          isPacked={true} 
          name="Kaciga sa zlatnim listom" 
        />
        <Item 
          isPacked={false} 
          name="Fotografija od Tam" 
        />
      </ul>
    </section>
  );
}

JavaScript && izraz vraća vrednost svoje desne strane (u našem slučaju, kvačicu) ako je leva strana (naš uslov) true. Ali, ako je uslov false, ceo izraz postaje false. React tumači false kao “rupu” u JSX stablu, kao null ili undefined, i ne renderuje ništa na tom mestu.

Pitfall

Ne koristite brojeve na levoj strani od &&.

Da bi se testirao uslov, JavaScript automatski konvertuje levu stranu u boolean. Međutim, ako je leva strana 0, onda ceo izraz dobija tu vrednost (0), a React će radije renderovati 0 nego ništa.

Na primer, česta greška je messageCount && <p>New messages</p>. Lako je pretpostaviti da to neće renderovati ništa kad messageCount ima vrednost 0, ali će zapravo renderovati 0!

Da biste to popravili, koristite boolean na levoj strani: messageCount > 0 && <p>New messages</p>.

Uslovna dodela JSX-a u promenljivu

Kada vas prečice sprečavaju da pišete običan kod, pokušajte da koristite if iskaz i promenljivu. Možete dodeljivati vrednost promenljivama definisanim sa let, pa počnite sa default sadržajem koji želite prikazati, imenom:

let itemContent = name;

Koristite if iskaz da ponovo dodelite JSX izraz u itemContent ako je isPacked true:

if (isPacked) {
itemContent = name + " ✅";
}

Vitičaste zagrade otvaraju “prozor u JavaScript”. Ugradite promenljivu sa vitičastim zagradama u povratno JSX stablo ugnježdavanjem prethodno izračunatog izraza unutar JSX-a:

<li className="item">
{itemContent}
</li>

Ovaj stil je najopširniji, ali je i najfleksibilniji. Evo primera:

function Item({ name, isPacked }) {
  let itemContent = name;
  if (isPacked) {
    itemContent = name + " ✅";
  }
  return (
    <li className="item">
      {itemContent}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Lista za pakovanje od Sally Ride</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Svemirsko odelo" 
        />
        <Item 
          isPacked={true} 
          name="Kaciga sa zlatnim listom" 
        />
        <Item 
          isPacked={false} 
          name="Fotografija od Tam" 
        />
      </ul>
    </section>
  );
}

Kao i ranije, ovo ne radi samo za tekst, već i za proizvoljan JSX:

function Item({ name, isPacked }) {
  let itemContent = name;
  if (isPacked) {
    itemContent = (
      <del>
        {name + " ✅"}
      </del>
    );
  }
  return (
    <li className="item">
      {itemContent}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Lista za pakovanje od Sally Ride</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Svemirsko odelo" 
        />
        <Item 
          isPacked={true} 
          name="Kaciga sa zlatnim listom" 
        />
        <Item 
          isPacked={false} 
          name="Fotografija od Tam" 
        />
      </ul>
    </section>
  );
}

Ako niste upoznati sa JavaScript-om, ovolika raznovrsnost stilova može delovati preobimno na početku. Međutim, učenje tih stilova će vam pomoći da čitate i pišete bilo koji JavaScript kod — ne samo React komponente! Za početak izaberite jedan, a kasnije pogledajte ovaj članak ponovo ako zaboravite kako ostali funkcionišu.

Recap

  • U React-u, logiku grananja kontrolišete pomoću JavaScript-a.
  • Možete vratiti JSX izraz uslovno pomoću if iskaza.
  • Možete uslovno sačuvati neki JSX u promenljivu, a onda ga uključiti unutar drugog JSX-a pomoću vitičastih zagrada.
  • U JSX-u, {cond ? <A /> : <B />} znači “ako je cond, renderuj <A />, u suprotnom <B />.
  • U JSX-u, {cond && <A />} znači “ako je cond, renderuj <A />, u suprotnom ništa”.
  • Prečice su česte, ali ne morate ih koristiti ako preferirate običan if.

Izazov 1 od 3:
Prikazati ikonicu za proizvode koji nisu spakovani pomoću ? :

Koristite uslovni operator (cond ? a : b) da renderujete ❌ ako isPacked nije true.

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✅'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Lista za pakovanje od Sally Ride</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Svemirsko odelo" 
        />
        <Item 
          isPacked={true} 
          name="Kaciga sa zlatnim listom" 
        />
        <Item 
          isPacked={false} 
          name="Fotografija od Tam" 
        />
      </ul>
    </section>
  );
}