Zum Inhalt
wannacode

Props: Daten von oben

Kurz gesagt: Props sind die Daten, die eine Komponente von ihrer Elternkomponente bekommt, so wie eine Funktion Parameter bekommt; die Kindkomponente ändert sie nicht, und children reicht Inhalt von außen herein.

Problem

Dreimal Brot

Der Katalog soll drei Produkte zeigen: Brot, Milch und Käse. Die Produktkarte ist aus der letzten Lektion fertig, also setzt App sie dreimal ein.

React

Alle drei Karten sind Brot. Das Produkt steht in ProductCard selbst, und jede Karte zeigt es. Der andere Weg wären BreadCard, MilkCard und CheeseCard: drei Kopien derselben Komponente, und jede Änderung an der Karte müsste dreimal gemacht werden. Es fehlt ein Weg, der Karte zu sagen, welches Produkt sie zeigen soll.

Die Idee

Props: Daten von oben

Eine Komponente ist eine Funktion. Eine Funktion bekommt ihre Daten als Parameter, und eine Komponente bekommt sie als Attribute von ihrer Elternkomponente: der Komponente, die sie benutzt. Diese Attribute heißen Props (von „properties“). Die Elternkomponente schreibt sie, die Kindkomponente liest sie.

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

React sammelt alle Attribute in einem Objekt und übergibt es der Funktion: { product: bread }. Die Schreibweise ({ product }) nimmt daraus die Eigenschaft product heraus (Destructuring). Ohne die geschweiften Klammern ist der Parameter das ganze Objekt: function ProductCard(props), und dann props.product.name.

Es gibt eine Karte, und jede Verwendung hat andere Daten. Bei einer Funktion ist es genauso: eine Definition, viele Aufrufe mit unterschiedlichen Argumenten.

Sieh es dir an

Sieh, wie sie fließen

const bread = { name: 'Weißbrot', price: 1.2 };
const milk = { name: 'Frischmilch', price: 1.6 };
const cheese = { name: 'Weißkäse', price: 7.9 };
function ProductCard({ product, discount = 0 }) {
return (
<article>
<h2>{product.name}</h2>
<p>Preis: {money(product.price * (1 - discount / 100))}</p>
</article>
);
}
export default function App() {
return (
<main>
<h1>Brot und Milch</h1>
<div>
<ProductCard product={bread} />
<ProductCard product={milk} />
<ProductCard product={cheese} discount={10} />
</div>
</main>
);
}
Schritt für Schritt, in Worten
  1. App hält die Daten: drei Produkte. Darunter stehen drei ProductCards: dieselbe Komponente dreimal, und keine weiß schon, was sie zeigen soll.
  2. product={bread} übergibt das Objekt bread an die erste Karte. Die Attribute einer Komponente heißen Props.
  3. Die anderen beiden Karten bekommen ihr eigenes Produkt. Die dritte bekommt außerdem discount = 10. Jede Karte hat ihre eigenen Props.
  4. Jede Karte zeichnet sich aus ihren Props. Brot und Milch haben keinen discount bekommen, deshalb ist er standardmäßig 0. Der Käse ist um 10 % reduziert: 7,11 €.
  5. Versucht die Karte props.discount = 0, geht nichts nach oben: Im Entwicklungsmodus wirft React einen TypeError (das Props-Objekt ist eingefroren), und die Daten in App bleiben gleich.
  6. Daten fließen nur in eine Richtung: von der Elternkomponente nach unten zur Kindkomponente. Um oben etwas zu ändern, muss die Kindkomponente die Elternkomponente bitten. Das ist das Modul „Zustand“.

Jede Karte kennt nur ihre eigenen Props. Sie sieht die anderen Karten nicht und weiß nicht, woher die Daten in App kommen.

Sieh es dir an

Eine Karte, drei Produkte

React

Die Produkte stehen in App, und es gibt eine ProductCard. Füge in App ein viertes Produkt hinzu (das Bild img/apples.svg gibt es) und noch ein <ProductCard … />. Die Karte fasst du nicht an. Bei 30 Produkten willst du eine Liste statt 30 Zeilen: Das ist die nächste Lektion.

Probier es aus

Finde den Fehler

Die Karte erscheint nicht, und in der Konsole steht „TypeError: Cannot read properties of undefined (reading 'toFixed')“.

Wähle die Zeile mit dem Fehler.

Die Idee

Ein Standardwert

Manche Produkte sind im Angebot. Die Karte bekommt ein zweites Prop, discount, in Prozent. Die meisten Produkte haben keinen Rabatt, und es ist nicht schön, wenn jede Zeile in App discount={0} schreiben muss. Ein fehlendes Prop ist undefined, wie ein fehlendes Argument einer Funktion. Der Preis wird zu price * (1 - undefined / 100), also NaN.

Deshalb bekommt das Prop einen Standardwert:

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

Das ist ein Standardwert im JavaScript-Destructuring: Fehlt discount, ist es 0. In älterem Code siehst du ProductCard.defaultProps = { discount: 0 } und propTypes. React 19 beachtet sie bei Funktionskomponenten nicht mehr: Typen zu prüfen ist die Aufgabe von TypeScript.

Probier es aus

Ein Rabatt

App gibt dem Käse einen discount von 10 (Prozent). Sorge dafür, dass die Karte discount annimmt, standardmäßig 0, und den Preis mit Rabatt zeigt: das Brot 1,20 €, die Milch 1,60 €, der Käse 7,11 €.

React

Die Idee

Schreibgeschützt

Die Daten gehören dem, der sie hält. Die Karte kann ihre Props lesen, aber nicht ändern. Probier den Button „Noch eins“: Die Kindkomponente versucht, ihr eigenes qty zu erhöhen.

React

Die Menge bleibt 2, und die Konsole meldet „TypeError: Cannot assign to read only property 'qty' of object“. Im Entwicklungsmodus friert React das Props-Objekt ein, genau um solchen Code zu fangen.

Selbst wenn es durchginge, wäre die Änderung nur in der Kindkomponente: App würde weiter 2 sagen und bei der nächsten Zeichnung wieder 2 übergeben. Zwei Orte mit verschiedenen Wahrheiten sind der Fehler aus „Eine Liste aus Daten“. Die Regel: Daten fließen nach unten, und sie zu ändern ist Sache der Elternkomponente. Wie eine Kindkomponente ihre Elternkomponente bittet, etwas zu ändern, ist das Modul „Zustand“.

Die Idee

Inhalt von außen

Der Shop hat Rahmen, die gleich aussehen und Verschiedenes enthalten: „Lieferung“ mit zwei Absätzen, „Rückgabe“ mit einem. Der Titel ist ein Prop. Der Inhalt ist ein ganzes Stück HTML, und es ist handlicher, es zwischen das öffnende und das schließende Tag zu schreiben, wie bei <section>.

<Card title="Lieferung">
<p>Innerhalb von 2 Werktagen.</p>
</Card>
export default function Card({ title, children }) {
return (
<section className="card">
<h2>{title}</h2>
{children}
</section>
);
}

Alles zwischen <Card> und </Card> kommt im besonderen Prop children an. Card entscheidet, wohin es das setzt.

React

Probier es aus

Der Rahmen

Card zeigt nur den Titel. Sorge dafür, dass es unter dem Titel auch den Inhalt zeigt, den App zwischen <Card> und </Card> schreibt.

React
Genauer, für Neugierige

Mehr als eine Stelle für Inhalt. Es gibt ein children. Hat der Rahmen auch einen unteren Teil, ist das ein weiteres Prop mit JSX: <Card title="Lieferung" footer={<a href="/delivery">Details</a>}>…</Card>, und Card setzt {footer} an die richtige Stelle.

Alle Eigenschaften auf einmal. <ProductCard {...product} /> übergibt jede Eigenschaft des Objekts als eigenes Prop: name, price, image. Das ist praktisch, aber man sieht nicht, was genau die Karte bekommt.

Nur die erste Ebene ist eingefroren. props.qty = 3 wirft einen Fehler, aber props.product.price = 2 geht still durch: Das Produktobjekt ist nicht eingefroren, es ist dasselbe Objekt wie in App. So ändert die Kindkomponente die Daten der Elternkomponente hinter deren Rücken. Tu das nicht.

Typen. React 19 prüft propTypes bei Funktionskomponenten nicht mehr und nutzt defaultProps für sie nicht. Die Typen der Props schreibt man mit TypeScript: Das kommt in der Lektion über ein echtes Projekt.

Übung

Das Schaufenster

Bau das Schaufenster von Grund auf. ProductCard nimmt product und discount (standardmäßig 0) und zeigt das Bild (alt ist der Name), den Namen als Überschrift der Ebene zwei und „Preis: …“ mit dem Rabatt. Card nimmt title und zeigt den Titel (Ebene zwei) und den Inhalt von außen. In App, unter der Überschrift: die drei Karten in einem div (der Käse mit discount 10), dann eine Card „Lieferung“ mit „Innerhalb von 2 Werktagen.“ und „Kostenlos ab 30 €.“.

React

Test

Teste dich

1. Was sind Props?
2. Was bekommt die Karte von <ProductCard product="bread" />?
3. Die Karte bekommt qty und will 1 dazuzählen. Was stimmt?
4. App schreibt <Card title="Lieferung"><p>Innerhalb von 2 Werktagen.</p></Card>. Wie zeigt Card den Absatz?
5. App schreibt <ProductCard product={bread} /> ohne discount. Was ist discount in der Karte?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·