Към съдържанието
wannacode

Props: данни отгоре

Накратко: Props са данните, които компонентът получава от родителя, както функцията получава параметри; детето не ги сменя, а children подава съдържание отвън.

Проблем

Три пъти хляб

Каталогът трябва да покаже три продукта: хляб, мляко и сирене. Картата на продукта е готова от миналия урок, затова App я слага три пъти.

React

И трите карти са хляб. Продуктът е записан вътре в ProductCard, и всяка карта показва него. Другият път е BreadCard, MilkCard и CheeseCard: три копия на един и същ компонент, и всяка промяна на картата трябва да се направи три пъти. Липсва начин да кажеш на картата кой продукт да покаже.

Идеята

Props: данни отгоре

Компонентът е функция. Функцията получава данните си като параметри, а компонентът ги получава като атрибути от родителя: компонента, който го ползва. Тези атрибути се казват props (от properties). Родителят ги пише, детето ги чете.

App.jsx
<ProductCard product={bread} />
<ProductCard product={milk} />
ProductCard.jsx
export default function ProductCard({ product }) {
return <h2>{product.name}</h2>;
}

React събира всички атрибути в един обект и го подава на функцията: { product: bread }. Записът ({ product }) взима свойството product от него (destructuring). Без скобите параметърът е целият обект: function ProductCard(props) и после props.product.name.

Картата е една, а всяко ползване е с други данни. Така е и с функцията: една дефиниция, много извиквания с различни аргументи.

Виж го

Виж как текат

const bread = { name: 'Бял хляб', price: 1.2 };
const milk = { name: 'Прясно мляко', price: 1.6 };
const cheese = { name: 'Сирене', price: 7.9 };
function ProductCard({ product, discount = 0 }) {
return (
<article>
<h2>{product.name}</h2>
<p>Цена: {money(product.price * (1 - discount / 100))}</p>
</article>
);
}
export default function App() {
return (
<main>
<h1>Хляб и мляко</h1>
<div>
<ProductCard product={bread} />
<ProductCard product={milk} />
<ProductCard product={cheese} discount={10} />
</div>
</main>
);
}
Стъпка по стъпка, с думи
  1. App държи данните: три продукта. Под него са три ProductCard: един и същи компонент три пъти, и никой още не знае какво да покаже.
  2. product={bread} подава обекта bread на първата карта. Атрибутите на компонента се казват props.
  3. Другите две карти получават своя продукт. Третата получава и discount = 10. Всяка карта има свои props.
  4. Всяка карта се рисува от своите props. Хлябът и млякото не са получили discount, затова е 0 по подразбиране. Сиренето е с 10% отстъпка: 7,11 €.
  5. Ако картата опита props.discount = 0, нагоре не тръгва нищо: в режима за разработка React хвърля TypeError (обектът с props е замразен), а данните в App остават същите.
  6. Данните текат само в една посока: от родителя надолу към детето. За да се смени нещо горе, детето трябва да помоли родителя. Това е модулът „Състояние“.

Всяка карта знае само своите props. Не вижда другите карти и не знае откъде идват данните в App.

Виж го

Една карта, три продукта

React

Продуктите са в App, а ProductCard е един. Добави четвърти продукт в App (картинката img/apples.svg има) и още един <ProductCard … />. Картата не я пипаш. При 30 продукта ще искаш списък, а не 30 реда: той е следващият урок.

Пробвай

Намери грешката

Картата не се показва, а в конзолата пише „TypeError: Cannot read properties of undefined (reading 'toFixed')“.

Избери реда с грешката.

Идеята

Стойност по подразбиране

Някои продукти са с отстъпка. Картата получава втори prop, discount, в проценти. Повечето продукти нямат отстъпка и не е хубаво всеки ред в App да пише discount={0}. Ако prop липсва, той е undefined, както липсващ аргумент на функция. Цената става price * (1 - undefined / 100), тоест NaN.

Затова prop-ът получава стойност по подразбиране:

export default function ProductCard({ product, discount = 0 }) {

Това е стойност по подразбиране при destructuring в JavaScript: ако discount липсва, е 0. В по-стар код ще видиш ProductCard.defaultProps = { discount: 0 } и propTypes. React 19 вече не ги поглежда за функционални компоненти: проверката на типовете е работа на TypeScript.

Пробвай

Отстъпка

App подава на сиренето discount 10 (процента). Направи картата да приема discount, 0 по подразбиране, и цената да е с отстъпката: хлябът 1,20 €, млякото 1,60 €, сиренето 7,11 €.

React

Идеята

Само за четене

Данните са на този, който ги държи. Картата може да чете своите props, но не и да ги сменя. Пробвай бутона „Още едно“: детето опитва да увеличи своя qty.

React

Броят остава 2, а конзолата казва „TypeError: Cannot assign to read only property 'qty' of object“. В режима за разработка React замразява обекта с props, точно за да хване такъв код.

Дори да мине, промяната щеше да е само в детето: в App пак щеше да пише 2, и при следващото рисуване App щеше да подаде 2 наново. Две места с различна истина са бъгът от „Списък от данни“. Правилото е: данните текат надолу, а промяната е работа на родителя. Как детето моли родителя да смени нещо, е модулът „Състояние“.

Идеята

Съдържание отвън

Магазинът има рамки, които изглеждат еднакво, а вътре е различно: „Доставка“ с два абзаца, „Връщане“ с един. Заглавието е prop. Съдържанието е цял HTML и е по-удобно да се пише между отварящия и затварящия таг, както при <section>.

<Card title="Доставка">
<p>До 2 работни дни.</p>
</Card>
export default function Card({ title, children }) {
return (
<section className="card">
<h2>{title}</h2>
{children}
</section>
);
}

Всичко между <Card> и </Card> идва в специалния prop children. Card решава къде да го сложи.

React

Пробвай

Рамката

Card показва само заглавието. Направи го да показва и съдържанието, което App пише между <Card> и </Card>, под заглавието.

React
По-точно, за любопитните

Повече от едно място за съдържание. children е един. Ако рамката има и долна част, тя е още един prop с JSX: <Card title="Доставка" footer={<a href="/delivery">Подробно</a>}>…</Card>, а Card слага {footer} където трябва.

Всички свойства наведнъж. <ProductCard {...product} /> подава всяко свойство на обекта като отделен prop: name, price, image. Удобно е, но не се вижда какво точно получава картата.

Замразено е само първото ниво. props.qty = 3 хвърля грешка, но props.product.price = 2 минава тихо: обектът на продукта не е замразен, той е същият обект като в App. Така детето сменя данните на родителя зад гърба му. Не го прави.

Типовете. React 19 вече не проверява propTypes на функционалните компоненти и не ползва defaultProps за тях. Типовете на props се пишат с TypeScript: той е в урока за истинския проект.

Упражнение

Витрината

Направи витрината от празно. ProductCard получава product и discount (0 по подразбиране) и показва картинката (alt е името), името като заглавие от второ ниво и „Цена: …“ с отстъпката. Card получава title и показва заглавието (второ ниво) и съдържанието отвън. В App, под заглавието: трите карти в div (сиренето с discount 10), после Card „Доставка“ с „До 2 работни дни.“ и „Безплатна над 30 €.“.

React

Тест

Провери се

1. Какво са props?
2. Какво получава картата от <ProductCard product="bread" />?
3. Картата получава qty и иска да го увеличи с 1. Кое е вярно?
4. App пише <Card title="Доставка"><p>До 2 работни дни.</p></Card>. Как Card показва абзаца?
5. App пише <ProductCard product={bread} /> без discount. Какво е discount в картата?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·