IlmHamroh
JavaScript Full-stack/17-qism. React asoslari4/45-dars21 daqiqa
Mundarija (33)

JSX II: ifodalar, Fragment va nima ekranga chiqadi — 0 && tuzog'i va XSS himoyasi

Qisqacha: JSX'dagi {} ichiga istalgan ifoda yoziladi, lekin har qiymat turi ekranda o'zicha chiqadi. Satr va son — matn bo'lib; null, undefined, true va false — umuman ko'rinmaydi; massiv — elementlari ketma-ket. 0 va NaN esa ko'rinadi — shuning uchun {count && <b>…</b>} bo'sh savatda 0 chiqaradi. Oddiy obyekt chizilmaydi, React xato tashlaydi. Ortiqcha teg qo'shmasdan guruhlash uchun — <>…</> (Fragment), ro'yxatda esa <Fragment key={…}>. JSX satrlarni har doim matn sifatida qo'yadi, shuning uchun innerHTML dagi XSS bu yerda ishlamaydi.

Bu darsda

  • {} ga qo'yilgan qiymat ekranda qanday chiqishini oldindan ayta olasiz: satr, son, boolean, null, massiv, obyekt.
  • 0 && va NaN && tuzog'ini taniysiz va uch xil yo'l bilan tuzatasiz.
  • <>…</> va <Fragment key> ni qachon ishlatishni bilasiz.
  • JSX ichiga to'g'ri izoh yozasiz.
  • JSX nega XSS'dan himoya qilishini va qayerda himoya tugashini tushuntira olasiz.

Oldin bilishingiz kerak: JSX I: JSX aslida JavaScript, Mantiqiy operatorlar va qisqa tutashuv, Kontentni o'zgartirish va XSS xavfi.

1. Nega bu kerak?

Oldingi darsda JSX jsx() chaqiruviga aylanishini, {} esa JavaScript'ga eshik ekanini ko'rdik. Endi savol: eshikdan nima kirsa, ekranda nima chiqadi?

Sardor «Bahor» saytiga savat yozdi: savatda taom bo'lsa, "3 ta taom" yozuvi chiqsin. Kod qisqa va chiroyli edi: {count && <b>{count} ta taom</b>}. Ertasiga Jasur aka telefon qildi: "Savat bo'sh bo'lsa, ekranda nega yolg'iz 0 turibdi?"

Vanilla JavaScript'da qoida bitta edi: textContent = qiymat — qiymat satrga aylanadi va chiqadi. React'da har tur o'z qoidasiga ega. Ba'zi qiymatlar yashirinadi, ba'zilari ko'rinib qoladi, ba'zilari butun komponentni yiqitadi. Bugun shu jadvalni to'liq yig'amiz.

Hayotiy o'xshatish: kassadagi chek printeri. Unga taom nomi bersangiz — chop etadi. Son bersangiz — chop etadi. "Hech narsa" belgisini bersangiz — qatorni o'tkazib yuboradi. Butun qog'oz papkani tiqsangiz — to'xtab, xato beradi. React'ning {} i ham shunday printer.

2. {} ichidagi ifodalar

2.1 Qisqa takror

{} ichiga faqat ifoda yoziladi — qiymat beradigan kod (JSX I darsida if nega sig'masligini ko'rdik). JavaScript'dan bilgan ifodalaringizning hammasi ishlaydi: o'zgaruvchi, arifmetika, funksiya chaqiruvi, ternary, &&, || va ??.

jsx
function App() {
  const note = null;
  const name = "";
  return (
    <div>
      <p>Izoh: {note ?? "yo'q"}</p>
      <p>Mehmon: {name || "nomsiz"}</p>
      <p>{name.length > 0 ? name : "Ism kiritilmagan"}</p>
    </div>
  );
}

Ekranda:

text
Izoh: yo'q
Mehmon: nomsiz
Ism kiritilmagan

?? faqat null va undefined da zaxira qiymatni oladi, || esa har qanday falsy qiymatda — bo'sh satrda ham (?? va ?. darsida). JSX bu yerda hech narsa o'zgartirmaydi: avval JavaScript ifodani hisoblaydi, keyin React natijani chizadi.

2.2 Element ham qiymat

<b>Yangi</b> — oddiy obyekt (oldingi darsda konsolda ko'rdik). Demak uni o'zgaruvchiga saqlash, funksiyadan qaytarish yoki massivga qo'yish mumkin:

jsx
function App() {
  const isNew = true;
  const badge = <b>Yangi!</b>;
  return (
    <p>
      Manti {isNew ? badge : null}
    </p>
  );
}

Ekranda:

text
Manti Yangi!

null — "bu yerda hech narsa chizma" degani. Nega aynan shunday — keyingi bo'limda.

3. Nima ekranga chiqadi

3.1 Har tur bitta qatorda

Hamma turni bitta ro'yxatda sinaymiz:

jsx
function App() {
  return (
    <ul>
      <li>satr: {"Osh"}</li>
      <li>son: {35000}</li>
      <li>true: {true}</li>
      <li>false: {false}</li>
      <li>null: {null}</li>
      <li>undefined: {undefined}</li>
      <li>nol: {0}</li>
      <li>NaN: {NaN}</li>
      <li>bo'sh satr: [{""}]</li>
      <li>massiv: {["Osh", "Manti", 3]}</li>
    </ul>
  );
}

Ekranda:

text
satr: Osh
son: 35000
true:
false:
null:
undefined:
nol: 0
NaN: NaN
bo'sh satr: []
massiv: OshManti3

Natijani jadvalga yig'amiz:

Qiymat Ekranda Izoh
"Osh", 35000 matn odatdagidek
true, false hech narsa && uchun ataylab
null, undefined hech narsa "chizma" belgisi
0, NaN 0, NaN son — ko'rinadi!
"" hech narsa bo'sh matn
massiv elementlari ketma-ket ajratuvchisiz
oddiy obyekt xato pastda

Uch narsaga e'tibor bering.

Boolean ko'rinmaydi. {true} ekranga "true" yozmaydi. Bu ataylab qilingan: shunda {isOpen && <p>Ochiq</p>} yozish mumkin. isOpen false bo'lsa, ifoda false ga teng va React uni o'tkazib yuboradi.

Son har doim ko'rinadi. 0 ham son, NaN ham. React sonni "hech narsa" deb hisoblamaydi — ehtimol siz aynan nolni ko'rsatmoqchisiz ("Savatda: 0 ta").

Massiv elementlari yopishadi. ["Osh", "Manti", 3] → OshManti3. React massivni vergul bilan ulamaydi — har elementni alohida bola sifatida chizadi. Vergul yoki ro'yxat kerak bo'lsa — o'zingiz yasaysiz: .join(", ") yoki .map() bilan <li> lar (pastda).

Maslahat: React 19 da BigInt ham son kabi chiqadi: {10n} → 10. {-0} esa 0 bo'lib chiqadi.

Tekshirib ko'ring: <p>{"0"}</p> va <p>{0}</p> farqi bormi? <p>{[]}</p> nima chiqaradi?

Javob

Ekranda farq yo'q — ikkalasi ham 0. Lekin sabab boshqa: "0" — satr, 0 — son, ikkalasi ham matn bo'lib chiqadi. Farq && bilan paydo bo'ladi: "0" truthy (bo'sh bo'lmagan satr), 0 esa falsy. {[]} — bo'sh massiv, chizadigan elementi yo'q — ekranda hech narsa.

4. 0 && tuzog'i

4.1 Tuzoqni jonli ko'ramiz

Avval JavaScript'ni eslaymiz (Mantiqiy operatorlar darsi). a && b true/false qaytarmaydi. a falsy bo'lsa — a ning o'zini qaytaradi, aks holda b ni. Demak 0 && "ko'p" → 0, false && "ko'p" → false.

Endi Sardorning savati. Quyidagi misolda ikkita tugma bor. onClick — bosilganda chaqiriladigan funksiya (Hodisalarni boshqarish darsida to'liq). draw() esa Birinchi React loyiha darsidagi root.render ni yangi count bilan qayta chaqiradi. Bu vanilla uslubi; React'ning o'z xotirasi — state — useState darsida.

jsx
import { createRoot } from "react-dom/client";

const root = createRoot(document.getElementById("root"));
let count = 0;

function draw() {
  root.render(
    <div>
      <p>Savat: {count && <b>{count} ta taom</b>}</p>
      <button onClick={() => { count++; draw(); }}>+1</button>{" "}
      <button onClick={() => { count = 0; draw(); }}>Tozalash</button>
    </div>
  );
}
draw();

Ekranda:

text
Savat: 0
+1 Tozalash

«Ishga tushir» ni bosing va tugmalarni sinang. Har bosishda nima bo'lishi:

Qadam count count && … Ekranda
boshida 0 0 Savat: 0
«+1» 1 <b> elementi Savat: 1 ta taom
«Tozalash» 0 0 Savat: 0

Jasur aka ko'rgan yolg'iz 0 — aynan shu. JavaScript 0 qaytardi, React esa sonni chizdi.

4.2 NaN — o'sha tuzoq, qo'shimcha ogohlantirish bilan

Reyting hali hisoblanmagan bo'lsa, NaN bo'lishi mumkin (masalan 0 / 0):

jsx
function App() {
  const rating = NaN;
  return <p>{rating && <b>{rating} yulduz</b>}</p>;
}

Konsolda:

text
Received NaN for the `children` attribute. If this is expected, cast the value to a string.

Ekranda:

text
NaN

"children uchun NaN keldi. Agar bu kutilgan bo'lsa, qiymatni satrga aylantiring." React NaN ni chizdi, lekin bu deyarli har doim xato ekanini sezib, ogohlantirdi.

4.3 TypeScript ham ushlamaydi

tsx
export function App() {
  const count: number = 0;
  return <p>Savat: {count && <b>{count} ta taom</b>}</p>;
}

Ekranda:

text
Savat: 0

Tip xatosi yo'q: 0 — son, son esa {} ga qo'yish mumkin bo'lgan qiymat. TypeScript'dan bu yerda yordam kutmang — tuzoq mantiqda, turda emas.

4.4 Uch tuzatish

&& ning chap tomoni haqiqiy boolean bo'lsin:

  • taqqoslash: {count > 0 && <b>…</b>} — eng aniq;
  • ikki inkor: {!!count && <b>…</b>} yoki {Boolean(count) && …};
  • ternary: {count > 0 ? <b>…</b> : "bo'sh"} — bo'sh holatga ham matn kerak bo'lsa.

Ternary bilan tuzatilgan savat. Bu safar blok tugmalarni o'zi bosadi: avval «+1», keyin «Tozalash».

jsx
import { createRoot } from "react-dom/client";

const root = createRoot(document.getElementById("root"));
let count = 0;

function draw() {
  root.render(
    <div>
      <p>Savat: {count > 0 ? <b>{count} ta taom</b> : "bo'sh"}</p>
      <button onClick={() => { count++; draw(); }}>+1</button>{" "}
      <button onClick={() => { count = 0; draw(); }}>Tozalash</button>
    </div>
  );
}
draw();

Ekranda:

text
Savat: bo'sh
+1 Tozalash

Ko'pincha tuzoq .length bilan uchraydi: {orders.length && <List />}. Ro'yxat bo'sh bo'lsa — 0. Qoida bitta: && chapida son turgan bo'lsa, uni > 0 bilan taqqoslang. Shartli chizishning hamma usullari — Shartli render darsida.

5. Obyekt chizilmaydi

5.1 Objects are not valid as a React child

Sardor buyurtmani to'liq chiqarmoqchi bo'ldi:

jsx
function App() {
  const order = { dish: "Osh", price: 35000 };
  return <p>{order}</p>;
}

Konsolda:

text
Uncaught Error: Objects are not valid as a React child (found: object with keys {dish, price}). If you meant to render a collection of children, use an array instead.

"Obyektlar React bolasi bo'la olmaydi (topildi: dish, price kalitli obyekt). Bolalar to'plamini chizmoqchi bo'lsangiz, massiv ishlating." Saytda «Komponentni chizishda xato» qutisi chiqadi, ostida Maslahat va Komponentlar: App → p — xato qaysi komponent ichida ekani.

Nega React taxmin qilmaydi? Obyektni qanday ko'rsatish kerak — {"dish":"Osh"} mi, "Osh, 35000" mi, jadvalmi? Bunga faqat siz javob bera olasiz. Shuning uchun React jim qolmaydi, to'xtaydi. Tuzatish — kerakli maydonlarni o'zingiz tanlang: <p>{order.dish} — {order.price} so'm</p>.

Date ham obyekt: {new Date()} xuddi shu xatoni beradi (found: [object Date]). Sanani satrga aylantirib qo'ying.

5.2 Funksiya — ogohlantirish

Qavslarni unutish — boshqa ko'p uchraydigan xato:

jsx
function getPrice() {
  return 35000;
}

function App() {
  return <p>Narx: {getPrice} so'm</p>;
}

Konsolda:

text
Functions are not valid as a React child. This may happen if you return getPrice instead of <getPrice /> from render. Or maybe you meant to call this function rather than return it.
<p>{getPrice}</p>

Ekranda:

text
Narx: so'm

"Funksiyalar React bolasi bo'la olmaydi. … Balki bu funksiyani qaytarish o'rniga chaqirmoqchi edingiz?" Bu safar xato emas, ogohlantirish: funksiya o'rni bo'sh qoldi. Ikkinchi qator — xato aynan qayerda ekanini ko'rsatadi. Tuzatish — {getPrice()}.

5.3 TypeScript ikkalasini ham ushlaydi

tsx
function getPrice(): number {
  return 35000;
}

export function App() {
  const order = { dish: "Osh", price: 35000 };
  return (
    <p>
      {order} {getPrice}
    </p>
  );
}

Xato:

text
TS2322: Type '{ dish: string; price: number; }' is not assignable to type 'ReactNode'.
TS2322: Type '() => number' is not assignable to type 'ReactNode'.

ReactNode — {} ga qo'yish mumkin bo'lgan hamma qiymatlarning turi (@types/react dan). Unga kiradi: satr, son, bigint, boolean, null, undefined, element va shularning massivi. Oddiy obyekt ham, funksiya ham unda yo'q. Bu turni Props va children darslarida ko'p uchratasiz. 0 && tuzog'idan farqli, bu yerda TypeScript haqiqatan yordam beradi.

6. Massivni chizish

Massiv elementlari ketma-ket chiziladi — demak elementlar massivini ham chizsa bo'ladi. Ro'yxat odatda .map() bilan yasaladi:

jsx
const menu = [
  { id: 1, name: "Osh", price: 35000 },
  { id: 2, name: "Manti", price: 30000 },
];

function App() {
  return (
    <ul>
      {menu.map((dish) => (
        <li key={dish.id}>{dish.name} — {dish.price} so'm</li>
      ))}
    </ul>
  );
}

Ekranda:

text
Osh — 35000 so'm
Manti — 30000 so'm

Har obyekt <li> elementiga aylandi — obyekt emas, element massivi chizilyapti, xato yo'q. Yangi atribut key={dish.id} — React'ga har qatorni tanib olish uchun yorliq. JSX I darsida jsxs "qo'lda yozilgan, o'zgarmas massiv" ekanini aytgan edik. Map natijasi esa o'zgaruvchan — undagi har element key so'raydi. Kalitni unutsangiz:

jsx
function App() {
  const dishes = ["Osh", "Manti", "Lag'mon"];
  return <ul>{dishes.map((d) => <li>{d}</li>)}</ul>;
}

Konsolda:

text
Each child in a list should have a unique "key" prop.
Check the render method of `App`. See https://react.dev/link/warning-keys for more information.

Ekranda:

text
Osh
Manti
Lag'mon

"Ro'yxatdagi har bolada noyob key props'i bo'lishi kerak." Ekran to'g'ri, lekin ogohlantirish bejiz emas. key qoidalari va indeksni key qilishning xavfi — Ro'yxatlar va key darsida.

7. Fragment

7.1 Ortiqcha <div> siz guruhlash

JSX I darsida bitta ildiz qoidasini <div> bilan yechdik. Lekin ba'zan <div> HTML'ni buzadi:

  • <ul> ichida faqat <li> bo'lishi kerak;
  • jadval qatori <tr> ichida faqat <td> yoki <th>;
  • <dl> ichida <dt> va <dd> juftliklari;
  • Flexbox yoki Grid'da ortiqcha <div> bolalarning joylashuvini o'zgartiradi.

Fragment — "ko'rinmas o'ram": elementlarni guruhlaydi, lekin DOM'ga hech narsa qo'shmaydi. Qisqa yozuvi — <>…</>:

jsx
import { flushSync } from "react-dom";
import { createRoot } from "react-dom/client";

const root = createRoot(document.getElementById("root"));
flushSync(() => {
  root.render(
    <>
      <h1>Menyu</h1>
      <p>Bugun 4 ta taom</p>
    </>
  );
});
console.log(document.getElementById("root").innerHTML);

Konsolda:

text
<h1>Menyu</h1><p>Bugun 4 ta taom</p>

Ekranda:

text
Menyu
Bugun 4 ta taom

#root ichida o'ram yo'q — h1 va p to'g'ridan-to'g'ri. (flushSync — oldingi darsdagidek, natijani darhol ko'rish uchun.) esbuild'da <> nimaga aylanishi:

text
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
const page = /* @__PURE__ */ jsxs(Fragment, { children: [
  /* @__PURE__ */ jsx("h1", { children: "Menyu" }),
  /* @__PURE__ */ jsx("p", { children: "Bugun 4 ta taom" })
] });

<> — maxsus Fragment komponenti uchun qisqa yozuv. React uni ko'rib, faqat bolalarini chizadi.

7.2 Ro'yxatda Fragment: key kerak

Menyuni <dl> da ko'rsatamiz: har taomga ikkita element — <dt> (nomi) va <dd> (narxi). .map() har safar ikkita element qaytarishi kerak — demak Fragment:

jsx
const menu = [
  { id: 1, name: "Osh", price: 35000 },
  { id: 2, name: "Manti", price: 30000 },
];

function App() {
  return (
    <dl>
      {menu.map((m) => (
        <>
          <dt>{m.name}</dt>
          <dd>{m.price} so'm</dd>
        </>
      ))}
    </dl>
  );
}

Konsolda:

text
Each child in a list should have a unique "key" prop.
Check the render method of `App`. See https://react.dev/link/warning-keys for more information.

Ekranda:

text
Osh
35000 so'm
Manti
30000 so'm

Ro'yxat elementi endi Fragment — key unga kerak. Qisqa yozuvga atribut yozib bo'lmaydi:

jsx
function App() {
  const menu = [{ id: 1, name: "Osh" }];
  return (
    <dl>
      {menu.map((m) => (
        <key={m.id}>
          <dt>{m.name}</dt>
        </>
      ))}
    </dl>
  );
}

Saytda:

text
JSX sintaksis xatosi (5-qator, 22-ustun): Unexpected token, expected ","

<key= ni tahlilchi teg deb o'qimadi — <> dan boshqa narsani bilmaydi. Yechim — to'liq yozuv: Fragment ni react dan import qilib, key bering:

jsx
import { Fragment } from "react";

const menu = [
  { id: 1, name: "Osh", price: 35000 },
  { id: 2, name: "Manti", price: 30000 },
];

function App() {
  return (
    <dl>
      {menu.map((m) => (
        <Fragment key={m.id}>
          <dt>{m.name}</dt>
          <dd>{m.price} so'm</dd>
        </Fragment>
      ))}
    </dl>
  );
}

Ekranda:

text
Osh
35000 so'm
Manti
30000 so'm

Ogohlantirish yo'qoldi. Fragment boshqa atributlarni qabul qilmaydi: <Fragment className="menyu"> yozsangiz, React aytadi: React.Fragment can only have `key`, `ref`, and `children` props — "Fragment faqat key, ref va children qabul qiladi". Fragment DOM'da yo'q — className qo'yadigan joy ham yo'q. Eski kodda <React.Fragment key={…}> ni ko'rasiz — o'sha narsa, React obyekti orqali.

Tekshirib ko'ring: Qaysi holatda <> yetadi, qaysi birida <Fragment key> kerak? (a) komponent <h1> va <p> qaytaradi; (b) .map() har taomga <dt> va <dd> qaytaradi; (c) .map() har taomga bitta <li> qaytaradi.

Javob

(a) <> — ro'yxat emas, key kerak emas. (b) <Fragment key={m.id}> — ro'yxat elementi Fragment, key unga beriladi. (c) Fragment umuman kerak emas — <li key={…}> ning o'zi bitta ildiz.

8. JSX ichida izoh

HTML izohi <!-- --> JSX'da ishlamaydi:

jsx
function App() {
  return (
    <div>
      <!-- narxlar -->
      <p>Menyu</p>
    </div>
  );
}

Saytda:

text
JSX sintaksis xatosi (4-qator, 9-ustun): Unexpected token

JSX ichida izoh — JavaScript izohi, {} ichida:

jsx
function App() {
  return (
    <p /* teg ichida ham bo'ladi */ className="narx">
      {/* ko'p qatorli
          izoh */}
      Osh
      {
        // bir qatorli izoh
      }
    </p>
  );
}

Ekranda:

text
Osh

{/* … */} — eng ko'p ishlatiladigan shakl. Nega {} kerak? Teglar orasidagi hamma narsa — matn. /* izoh */ ni qavssiz yozsangiz, u ekranga matn bo'lib chiqadi. {} ichida esa JavaScript: izohdan keyin ifoda yo'q — bo'sh {} hech narsa chizmaydi. // izohdan keyin } alohida qatorda bo'lishi shart. Aks holda } ham izohga kirib ketadi va tahlilchi Unterminated JSX contents deydi.

9. JSX va XSS

9.1 Satr — har doim matn

Kontentni o'zgartirish va XSS xavfi darsida mehmon izohiga <img src="x" onerror="…"> yozilgan edi. innerHTML uni teg deb o'qidi va kod ishga tushdi:

js
// ❌ vanilla: satr HTML deb o'qiladi
izoh.innerHTML = comment;

Xuddi shu satrni JSX'ga beramiz:

jsx
import { flushSync } from "react-dom";
import { createRoot } from "react-dom/client";

const comment = '<img src="x" onerror="console.log(\'XSS\')">Zo\'r';
const root = createRoot(document.getElementById("root"));
flushSync(() => root.render(<p>Izoh: {comment}</p>));
console.log(document.getElementById("root").innerHTML);

Konsolda:

text
<p>Izoh: &lt;img src="x" onerror="console.log('XSS')"&gt;Zo'r</p>

Ekranda:

text
Izoh: <img src="x" onerror="console.log('XSS')">Zo'r

< va > &lt; va &gt; ga aylandi: brauzer ularni teg emas, harf deb ko'rsatdi. Mehmon izohni teg belgilari bilan birga ko'radi — hech qanday kod ishlamadi. Sabab: React DOM satrni matn tuguni sifatida qo'yadi, xuddi textContent kabi. {} dagi satr HTML bo'lib o'qilmaydi — hech qachon.

9.2 $$typeof — soxta elementdan himoya

JSX I darsida elementda $$typeof: Symbol(react.transitional.element) borligini ko'rdik. Endi uning foydasi. Server JSON qaytardi, unda element "shaklidagi" obyekt bor:

jsx
const fromServer = JSON.parse(
  '{"type": "img", "props": {"src": "x"}}'
);

function App() {
  return <p>{fromServer}</p>;
}

Konsolda:

text
Uncaught Error: Objects are not valid as a React child (found: object with keys {type, props}). If you meant to render a collection of children, use an array instead.

type va props bor, lekin React uni element deb qabul qilmadi. Symbol ni JSON'da yozib bo'lmaydi — demak tashqaridan kelgan matn $$typeof li obyekt yasay olmaydi. Hujumchi o'z "elementini" serverdagi ma'lumot ichiga yashirib kiritolmaydi.

Hujumchi nigohi

Hujumchi «Bahor» saytidagi izoh maydoniga zararli matn yozadi. Uning urinishlari va React'ning javobi:

  • Teg yozish (<img onerror>, <script>) — {} dagi satr matn bo'lib chiqadi. Himoyalangan.
  • Soxta element (JSON'da {type, props}) — $$typeof yo'q, React chizmaydi. Himoyalangan.
  • javascript: havola. Mehmon profilidagi "sayt" maydoniga javascript:alert(1) yozadi va u <a href={link}> ga tushadi. React 19 bunday manzilni almashtiradi. Tekshirib ko'rdik — href ichida javascript:throw new Error('React has blocked a javascript: URL as a security precaution.') paydo bo'ldi: "React xavfsizlik uchun javascript: manzilni to'sdi". Lekin bu oxirgi chiziq. Manzilni o'zingiz ham tekshiring: faqat https: bilan boshlanganini qabul qiling.
  • HTML'ni ataylab qo'yish. React'da innerHTML ning o'rnini bosuvchi atribut bor — dangerouslySetInnerHTML. Nomining o'zi ogohlantiradi: "xavfli". U himoyani butunlay o'chiradi — vanilla innerHTML dagi hamma XSS qaytib keladi. Foydalanuvchi matnini unga hech qachon to'g'ridan-to'g'ri bermang. Qachon va qanday tozalab ishlatish — Frontend xavfsizligi React'da darsida.

Diqqat: React faqat {} dagi matnni himoya qiladi. Atributlardagi manzil (href, src), dangerouslySetInnerHTML va uchinchi tomon skriptlari — sizning mas'uliyatingiz. XSS va DOM xavfsizligi darsidagi CSP va Trusted Types vazifalar da hozir ham ishlayapti — React buni o'zgartirmadi.

10. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
ekranda yolg'iz 0 {count && …}, count = 0 count > 0 && yoki ternary
ekranda NaN + ogohlantirish {rating && …}, rating = NaN Number.isNaN bilan tekshiring
Objects are not valid as a React child obyekt yoki Date {} da maydonni tanlang: {order.dish}
Functions are not valid as a React child {getPrice} {getPrice()}
OshManti yopishgan massiv ajratuvchisiz .join(", ") yoki <li> lar
unique "key" prop .map() da key yo'q key={id} (Fragment'ga ham)
Unexpected token <!-- da HTML izoh {/* … */}

11. Mashqlar

Mashqlar kurs/mashqlar/17/04-jsx-ifodalar/ da. Har yechimni «Ishga tushir» bilan sinang.

1-mashq (oson): Ekranda nima chiqadi?

Kodni ishga tushirmasdan, har qatorni oldindan ayting.

jsx
function App() {
  const guests = 0;
  const note = undefined;
  const dishes = ["Osh", "Manti"];
  return (
    <div>
      <p>A: {guests}</p>
      <p>B: {guests && "mehmon bor"}</p>
      <p>C: {note}</p>
      <p>D: {dishes}</p>
      <p>E: {dishes.length > 1 && "ko'p taom"}</p>
    </div>
  );
}
  • B qatorida, B: yorlig'idan keyin:
  • D qatorida, D: dan keyin:
  • E qatorida, E: dan keyin:
Yechim

Ekranda:

text
A: 0
B: 0
C:
D: OshManti
E: ko'p taom

A — son, ko'rinadi. B — 0 && … → 0, tuzoq. C — undefined, hech narsa. D — massiv, elementlari ajratuvchisiz. E — 2 > 1 → true, demak && o'ng tomonni qaytardi.

2-mashq (o'rta): Sardorning uchta xatosi

Sardor «Bahor» kassasi uchun panel yozdi. Kod yiqiladi, tuzatilsa ham ikki muammo qoladi. Uchalasini toping va tuzating: ekranda ofitsiant ismi va smenasi, buyurtma bo'lmasa "Hozircha buyurtma yo'q", menyu esa <dl> da chiqsin. Konsol toza bo'lsin.

jsx
const orders = [];
const waiter = { name: "Sardor", shift: "kechki" };
const menu = [
  { id: 1, name: "Osh", price: 35000 },
  { id: 2, name: "Lag'mon", price: 28000 },
];

function App() {
  return (
    <div>
      <p>Ofitsiant: {waiter}</p>
      <p>{orders.length && "Buyurtmalar bor"}</p>
      <dl>
        {menu.map((m) => (
          <>
            <dt>{m.name}</dt>
            <dd>{m.price} so'm</dd>
          </>
        ))}
      </dl>
    </div>
  );
}

Ishora: «Obyekt chizilmaydi», «0 && tuzog'i» va «Ro'yxatda Fragment» bo'limlari. Fragment'ga key uchun nimani import qilish kerak?

Yechim
jsx
import { Fragment } from "react";

const orders = [];
const waiter = { name: "Sardor", shift: "kechki" };
const menu = [
  { id: 1, name: "Osh", price: 35000 },
  { id: 2, name: "Lag'mon", price: 28000 },
];

function App() {
  return (
    <div>
      <p>Ofitsiant: {waiter.name} ({waiter.shift} smena)</p>
      <p>
        {orders.length > 0
          ? `${orders.length} ta buyurtma`
          : "Hozircha buyurtma yo'q"}
      </p>
      <dl>
        {menu.map((m) => (
          <Fragment key={m.id}>
            <dt>{m.name}</dt>
            <dd>{m.price} so'm</dd>
          </Fragment>
        ))}
      </dl>
    </div>
  );
}

Ekranda:

text
Ofitsiant: Sardor (kechki smena)
Hozircha buyurtma yo'q
Osh
35000 so'm
Lag'mon
28000 so'm
  1. {waiter} — obyekt, komponent yiqiladi. Maydonlarini chiqardik.
  2. {orders.length && …} — bo'sh massivda 0. Taqqoslash va ternary.
  3. <> ga key berib bo'lmaydi — Fragment import qilindi.

3-mashq (qiyin): Har qiymatni xavfsiz ko'rsatadigan yordamchi

«Bahor» dasturchilari uchun "kuzatuv paneli" yasang: unda turli qiymatlar va ularning ekrandagi ko'rinishi turadi. TypeScript'da showValue(value: unknown): string | number funksiyasini yozing. Talablar:

  • null, undefined, NaN va bo'sh satr → "—";
  • boolean → "ha" yoki "yo'q" (React ularni yashirgani uchun);
  • son va satr — o'zicha;
  • massiv → elementlari ", " bilan (har element ham shu qoidalar bilan);
  • qolgan hamma narsa (obyekt) → JSON.stringify.

Panel <dl> bo'lsin: har qiymatga <dt> (nomi) va <dd> (ko'rinishi), ro'yxat <Fragment key> bilan. Ishora: unknown ni typeof va Array.isArray bilan toraytiring (Narrowing: turni toraytirish darsi). string | number — ReactNode ning bir qismi, shuning uchun natijani {} ga bemalol qo'yasiz.

Yechim
tsx
import { Fragment } from "react";

function showValue(value: unknown): string | number {
  if (value === null || value === undefined) return "—";
  if (typeof value === "number") {
    return Number.isNaN(value) ? "—" : value;
  }
  if (typeof value === "string") return value === "" ? "—" : value;
  if (typeof value === "boolean") return value ? "ha" : "yo'q";
  if (Array.isArray(value)) return value.map(showValue).join(", ");
  return JSON.stringify(value);
}

const samples: [string, unknown][] = [
  ["Mehmonlar", 0],
  ["Reyting", NaN],
  ["Izoh", null],
  ["Ochiqmi", true],
  ["Taomlar", ["Osh", "Manti"]],
  ["Buyurtma", { dish: "Osh", soni: 2 }],
];

export default function App() {
  return (
    <dl>
      {samples.map(([label, value]) => (
        <Fragment key={label}>
          <dt>{label}</dt>
          <dd>{showValue(value)}</dd>
        </Fragment>
      ))}
    </dl>
  );
}

Ekranda:

text
Mehmonlar
0
Reyting
—
Izoh
—
Ochiqmi
ha
Taomlar
Osh, Manti
Buyurtma
{"dish":"Osh","soni":2}

Tekshiruv tartibi muhim: NaN ning turi number, shuning uchun u son tekshiruvi ichida ushlanadi. Massiv typeof da "object" — u JSON.stringify dan oldin tekshirilishi shart. value.map(showValue) — har element uchun o'sha funksiya (rekursiya). JSON.stringify natijasi satr — satr esa React'da xavfsiz: ichida <script> bo'lsa ham matn bo'lib chiqadi.

12. Real ishda

  • 0 && — kod ko'rigida (code review) tez-tez belgilanadigan xato. Uni avtomatik ushlash uchun lint qoidasi ham bor: eslint-plugin-react dagi react/jsx-no-leaked-render ("render'ga sizib chiqqan qiymat").
  • Fragment — jadval, ro'yxat va Flexbox/Grid maketlarida har kuni: ortiqcha <div> CSS'ni buzadi va DOM'ni og'irlashtiradi.
  • XSS: React'da matnni chiqarish sukut bo'yicha xavfsiz — vanilla innerHTML dagi xatoni tasodifan qilib bo'lmaydi. Shuning uchun xavfsizlik tekshiruvida (security review) birinchi qidiriladigan joylar — dangerouslySetInnerHTML va foydalanuvchidan kelgan href.
  • Intervyu savollari: "{count && <X/>} nega 0 chiqaradi?", "React qaysi qiymatlarni chizmaydi?", "Fragment nima va qachon key kerak?", "React XSS'dan qanday himoya qiladi va qayerda himoya qilmaydi?".

Xulosa

  • {} ichida istalgan ifoda; natija turiga qarab chiziladi.
  • null, undefined, true, false, "" — hech narsa; satr va son — matn; massiv — elementlari ketma-ket.
  • 0 va NaN ko'rinadi: && chapida son bo'lsa — > 0 bilan taqqoslang yoki ternary ishlating. TypeScript bu tuzoqni ushlamaydi.
  • Oddiy obyekt va Date — xato, funksiya — ogohlantirish; ReactNode turi ikkalasini ham rad etadi.
  • <>…</> DOM'ga hech narsa qo'shmaydi; ro'yxatda <Fragment key={…}>. Izoh — {/* … */}.
  • {} dagi satr hech qachon HTML bo'lib o'qilmaydi; dangerouslySetInnerHTML bu himoyani o'chiradi.

Keyingi dars: Komponentlar: yaratish, nomlash va fayllarga ajratish — JSX qaytaradigan funksiyalarni o'z komponentlarimizga aylantiramiz, katta harf qoidasini va fayllarga bo'lishni o'rganamiz.

Manbalar

  • React: "JavaScript in JSX with Curly Braces", "Conditional Rendering" (0 && tuzog'i), "Rendering Lists" — react.dev/learn
  • React: <Fragment> (<>...</>) — react.dev/reference/react/Fragment
  • React: "Common components" — dangerouslySetInnerHTML ogohlantirishi — react.dev/reference/react-dom/components/common
  • Dan Abramov: "Why Do React Elements Have a $$typeof Property?" (2018-12-03) — overreacted.io
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JSX II: ifodalar, Fragment va nima ekranga chiqadi — 0 && tuzog'i va XSS himoyasi — IlmHamroh