Умоўны рэндэрынг

Вашым кампанентам часта трэба будзе адлюстроўваць розныя рэчы ў залежнасці ад розных умоў. У React вы можаце ўмоўна рэндэрыць JSX, выкарыстоўваючы такія JavaScript аператары, як if, && і ?:.

You will learn

  • Як вярнуць розны JSX у залежнасці ад умовы
  • Як у залежнасці ад умоў уключаць або выключаць частку JSX
  • Распаўсюджаныя скарачэнні сінтаксісу ўмоўных выразаў, з якімі вы сутыкняцеся ў праектах на React.

Умоўнае вяртанне JSX

Дапусцім, у вас ёсць кампанент PackingList, які рэндэрыць некалькі элементаў Item, якія могуць быць пазначаны як упакаваныя ці не:

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

export default function PackingList() {
  return (
    <section>
      <h1>Спіс рэчаў Салі Райд</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Касмічны скафандр" 
        />
        <Item 
          isPacked={true} 
          name="Шлем з залатым лістом" 
        />
        <Item 
          isPacked={false} 
          name="Фота Тэма" 
        />
      </ul>
    </section>
  );
}

Звярніце ўвагу, што ў некаторых кампанентаў Item пропс isPacked мае значэнне true замест false. Калі isPacked={true}, мы хочам дадаць галачку(✔) да спакаваных рэчаў.

Вы можаце зрабіць гэта з дапамогай канструкцыі if/else такім чынам:

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

Калі пропс isPacked мае значэнне true, гэты код верне іншае дрэва JSX. Разам з гэтай зменай, некаторыя элементы атрымаюць галачку ў канцы:

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>Спіс рэчаў Салі Райд</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Касмічны скафандр" 
        />
        <Item 
          isPacked={true} 
          name="Шлем з залатым лістом" 
        />
        <Item 
          isPacked={false} 
          name="Фота Тэма" 
        />
      </ul>
    </section>
  );
}

Паспрабуйце адрэдагаваць тое, што вяртаецца ў кожным выпадку, і паглядзіце, як зменіцца вынік!

Звярніце ўвагу, як вы ствараеце разгалінаваную логіку з дапамогай JavaScript аператараў if і return. У React паток кіравання (напрыклад, умовы) апрацоўваецца JavaScript.

Умоўнае вяртанне нічога, з дапамогай null

У некаторых сітуацыях вам можа спатрэбіцца ўвогуле нічога не рэндэрыць. Напрыклад, вы наогул не хочаце паказваць спакаваныя рэчы. Але кампанент павінен нешта вяртаць. У гэтым выпадку вы можаце вярнуць null:

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

Калі isPacked мае значэнне true — кампанент нічога не вяртае, null. У адваротным выпадку ён верне JSX для рэндэрынгу.

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

export default function PackingList() {
  return (
    <section>
      <h1>Спіс рэчаў Салі Райд</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Касмічны скафандр" 
        />
        <Item 
          isPacked={true} 
          name="Шлем з залатым лістом" 
        />
        <Item 
          isPacked={false} 
          name="Фота Тэма" 
        />
      </ul>
    </section>
  );
}

На практыцы вяртанне null з кампанента не з’яўляецца звычайнай практыкай, таму што гэта можа здзівіць распрацоўшчыка, які спрабуе рэндэрыць яго. Часцей вы будзеце ўмоўна ўключаць або выключаць кампанент у JSX бацькоўскага кампанента. Вось як гэта можна зрабіць!

Умоўнае ўключэнне JSX

У папярэднім прыкладзе вы кантралявалі, якое JSX дрэва будзе вернута кампанентам (калі нешта будзе вернута ўвогуле!). Магчыма, вы ўжо заўважылі нейкае дубліраванне ў вывадзе рэндэрынгу:

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

вельмі падобна на

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

Абедзве галіны вяртаюць <li className="item">...</li>:

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

Хоць гэта дубліраванне і не шкоднае, але яно можа ўскладніць падтрыманне вашага кода. Што калі вы захочаце змяніць className? Вам трэба будзе зрабіць гэта ў двух месцах у вашым кодзе! У такой сітуацыі вы можаце ўмоўна ўключыць невялікі JSX, каб зрабіць свой код больш DRY (сухім).

Умоўны (тэрнарны) аператар (? :)

JavaScript мае кампактны сінтаксіс для запісу ўмоўнага выражэння — умоўны аператар або «тэрнарны аператар».

Замест гэтага:

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

Вы можаце напісаць гэта:

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

Гэта можна прачытаць як: «Калі ispacked роўны true, тады (?) рэндэрым name + ' ✔', інакш (:) рэндэрым name».

Deep Dive

Ці з’яўляюцца гэтыя два прыклады цалкам эквівалентнымі?

Калі вы знаёмы з аб’ектна-арыентаваным праграмаваннем, вы можаце выказаць здагадку, што два прыведзеныя вышэй прыклады крыху адрозніваюцца, таму што адзін з іх можа стварыць два розныя «асобнікі» <li>. Але JSX элементы не з’яўляюцца «асобнікамі», таму што яны не маюць ніякага ўнутранага стану і не з’яўляюцца сапраўднымі вузламі DOM. Гэта лёгкія апісанні, як чарцяжы. Такім чынам, гэтыя два прыклады, на самай справе, цалкам эквівалентныя. Захаванне і скідванне стану падрабязна распавядае пра тое, як гэта працуе.

Зараз дапусцім, што вы жадаеце абгарнуць завершаны тэкст элемента ў іншы HTML тэг, напрыклад <del>, каб выкрасліць яго. Вы можаце дадаць новыя радкі і круглыя дужкі, каб было лягчэй укладваць JSX у кожным з выпадкаў:

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

export default function PackingList() {
  return (
    <section>
      <h1>Спіс рэчаў Салі Райд</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Касмічны скафандр" 
        />
        <Item 
          isPacked={true} 
          name="Шлем з залатым лістом" 
        />
        <Item 
          isPacked={false} 
          name="Фота Тэма" 
        />
      </ul>
    </section>
  );
}

Гэты стыль добра працуе для простых умоў, але выкарыстоўвайце яго ў меру. Калі вашы кампаненты становяцца занадта заблытанымі з-за вялікай колькасці ўкладзенай умоўнай разметкі, падумайце аб вынясенні часткі кода ў даччыныя кампаненты, каб навесці парадак. У React разметка з’яўляецца часткай вашага кода, таму вы можаце выкарыстоўваць такія інструменты, як пераменныя і функцыі, каб упарадкаваць складаныя выразы.

Лагічны аператар І (&&)

Яшчэ адно скарачэнне, якое часта сустракаецца ў JavaScript — гэта лагічны аператар І (&&). Унутры кампанентаў React ён часта выкарыстоўваецца, калі вам трэба адрэндэрыць нейкі JSX, калі ўмова роўная true, або нічога не рэндэрыць інакш. З дапамогай && вы можаце ўмоўна рэндэрыць «птушку», толькі калі isPacked мае значэнне true:

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

Вы можаце чытаць гэта як «калі isPacked, тады (&&) рэндэрым «птушку», у адваротным выпадку — нічога не рэндэрым».

Вось яно ў дзеянні:

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

export default function PackingList() {
  return (
    <section>
      <h1>Спіс рэчаў Салі Райд</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Касмічны скафандр" 
        />
        <Item 
          isPacked={true} 
          name="Шлем з залатым лістом" 
        />
        <Item 
          isPacked={false} 
          name="Фота Тэма" 
        />
      </ul>
    </section>
  );
}

JavaScript выраз && вяртае значэнне правага боку (у нашым выпадку «птушка»), калі левы бок (наша ўмова) з’яўляецца true. Але калі наша ўмова — false, тады ўвесь выраз становіцца false. React разглядае false як «дзірку» ў дрэве JSX, зусім як null або undefined, і нічога не рэндэрыць на гэтым месцы.

Pitfall

Не стаўце лічбы злева ад &&.

Каб праверыць умову, JavaScript аўтаматычна пераўтворыць левы бок у булевае значэнне. Аднак, калі левы бок роўны 0, то ўвесь выраз атрымлівае гэта значэнне (0), і React з задавальненнем рэндэрыць 0 замест нічога.

Напрыклад, распаўсюджанай памылкай з’яўляецца напісанне кода накшталт messageCount && <p>Новыя паведамленні</p>. Лёгка выказаць здагадку, што ён нічога не рэндэрыць, калі messageCount роўны 0, але на самай справе ён рэндэрыць 0!

Каб выправіць гэта, зрабіце левы бок булевым значэннем: messageCount > 0 && <p>Новыя паведамленні</p>.

Умоўнае прысваенне JSX да пераменнай

Калі скарачэнні перашкаджаюць напісанню зразумелага кода, паспрабуйце выкарыстоўваць аператар if і пераменную. Вы можаце пераназначыць пераменныя, аб’яўленыя з дапамогай let, таму пачніце з прадастаўлення прадвызначанага змесціва, якое вы хочаце паказаць, напрыклад, name:

let itemContent = name;

Выкарыстоўвайце аператар if, каб пераназначыць JSX выраз itemContent, калі isPacked мае значэнне true:

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

Фігурныя дужкі «адчыняюць акенца» ў JavaScript. Устаўце пераменную з дапамогай фігурных дужак у JSX дрэва, якое вяртаецца з кампанента, уклаўшы раней вылічаны выраз унутр JSX:

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

Гэты стыль найбольш шматслоўны, але і найбольш гнуткі. Вось ён у дзеянні:

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

export default function PackingList() {
  return (
    <section>
      <h1>Спіс рэчаў Салі Райд</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Касмічны скафандр" 
        />
        <Item 
          isPacked={true} 
          name="Шлем з залатым лістом" 
        />
        <Item 
          isPacked={false} 
          name="Фота Тэма" 
        />
      </ul>
    </section>
  );
}

Як і раней, гэта працуе не толькі для тэксту, але і для адвольнага 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>Спіс рэчаў Салі Райд</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Касмічны скафандр" 
        />
        <Item 
          isPacked={true} 
          name="Шлем з залатым лістом" 
        />
        <Item 
          isPacked={false} 
          name="Фота Тэма" 
        />
      </ul>
    </section>
  );
}

Калі вы не знаёмыя з JavaScript, то такая разнастайнасць стыляў спачатку можа здацца ашаламляльнай. Аднак іх вывучэнне дапаможа вам чытаць і пісаць любы JavaScript код, а не толькі React кампаненты! Для пачатку абярыце той, які вам больш падабаецца, і пры неабходнасці звярніцеся да гэтага даведніка, калі забудзецеся, як працуюць іншыя.

Recap

  • У React вы кіруеце логікай галінавання з дапамогай JavaScript.
  • Вы можаце ўмоўна вярнуць JSX выраз з дапамогай аператара if.
  • Вы можаце ўмоўна захаваць JSX у пераменную, а потым уключыць яе ў іншы JSX, выкарыстоўваючы фігурныя дужкі.
  • У JSX выраз {cond ? <A /> : <B />} азначае «калі cond, рэндэрыць <A />, інакш <B />».
  • У JSX выраз{cond && <A />} азначае «калі cond, рэндэрыць <A />, інакш нічога».
  • Скарачэнні часта выкарыстоўваюцца, але вы не абавязаны выкарыстоўваць іх, калі аддаяце перавагу простаму if.

Challenge 1 of 3:
Паказаць значок для неспакаваных рэчаў з дапамогай ? :

Выкарыстоўвайце ўмоўны аператар (cond? a : b), каб паказаць ❌, калі isPacked не мае значэнне true.

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

export default function PackingList() {
  return (
    <section>
      <h1>Спіс рэчаў Салі Райд</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Касмічны скафандр" 
        />
        <Item 
          isPacked={true} 
          name="Шлем з залатым лістом" 
        />
        <Item 
          isPacked={false} 
          name="Фота Тэма" 
        />
      </ul>
    </section>
  );
}