6 Props - Datenübertragung zwischen Komponenten

Im vorherigen Kapitel haben Sie die Grundlagen von React-Komponenten kennengelernt. Sie verstehen jetzt, dass Komponenten JavaScript-Funktionen sind, die JSX zurückgeben. Diese Komponenten waren jedoch statisch – sie zeigten immer dieselben Inhalte an. In diesem Kapitel lernen Sie Props kennen, das Konzept, das Komponenten erst richtig mächtig und wiederverwendbar macht.

Props (kurz für “Properties”) sind der fundamentale Mechanismus in React, um Daten von einer Komponente an eine andere zu übertragen. Sie verwandeln statische Komponenten in dynamische, konfigurierbare Bausteine, die sich je nach übergebenen Daten unterschiedlich verhalten können.

6.1 Das Konzept verstehen: Props als Funktionsparameter

Der beste Weg, Props zu verstehen, ist der Vergleich mit Funktionsparametern in der regulären JavaScript-Programmierung. Betrachten Sie diese normale JavaScript-Funktion:

function greetUser(name, age) {
  return `Hallo ${name}, du bist ${age} Jahre alt!`;
}

greetUser("Anna", 25); // "Hallo Anna, du bist 25 Jahre alt!"
greetUser("Max", 30);  // "Hallo Max, du bist 30 Jahre alt!"

Eine React-Komponente mit Props funktioniert auf sehr ähnliche Weise:

function UserGreeting(props) {
  return <h1>Hallo {props.name}, du bist {props.age} Jahre alt!</h1>;
}

// Verwendung:
<UserGreeting name="Anna" age={25} />
<UserGreeting name="Max" age={30} />

Der entscheidende Unterschied liegt darin, dass React alle übergebenen Attribute automatisch in einem Objekt namens props sammelt und dieses als ersten Parameter an Ihre Komponenten-Funktion übergibt.

6.2 Props-Syntax und Datentypen

Props können verschiedene Datentypen übertragen. Die Syntax unterscheidet sich je nach Typ:

String-Props werden wie HTML-Attribute übergeben:

<Welcome message="Herzlich willkommen!" />

Numerische Props werden in geschweifte Klammern eingeschlossen:

<Counter startValue={10} />

Boolean-Props können verkürzt geschrieben werden:

<Button disabled />
// Equivalent zu: <Button disabled={true} />

Objekte und Arrays werden ebenfalls in geschweifte Klammern übergeben:

<UserCard user={{name: "Anna", email: "anna@example.com"}} />
<ProductList items={["Laptop", "Maus", "Tastatur"]} />

Diese Flexibilität macht Props extrem vielseitig. Sie können nahezu jeden JavaScript-Wert als Prop übergeben, einschließlich Funktionen – ein Konzept, das in späteren Kapiteln für Event-Handling wichtig wird.

6.3 TypeScript-Interfaces für Props

In professionellen React-Anwendungen hat sich TypeScript als Standard etabliert, da es Typsicherheit bietet und Entwicklungsfehler bereits zur Compile-Zeit erkennt. Für Props definieren Sie Interfaces, die genau beschreiben, welche Daten eine Komponente erwartet:

interface UserCardProps {
  name: string;
  age: number;
  email: string;
  isActive: boolean;
}

function UserCard(props: UserCardProps) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>Alter: {props.age}</p>
      <p>Email: {props.email}</p>
      <span>{props.isActive ? "Aktiv" : "Inaktiv"}</span>
    </div>
  );
}

Diese Interface-Definition bietet mehrere Vorteile: Sie dokumentiert, welche Props die Komponente benötigt, verhindert Tippfehler bei Prop-Namen und ermöglicht exzellente IDE-Unterstützung mit Autocompletion und Fehlerprüfung.

6.4 Props Destructuring: Elegantere Syntax

Anstatt immer props.name, props.age zu schreiben, können Sie JavaScript’s Destructuring-Syntax verwenden, um direkt auf die gewünschten Properties zuzugreifen:

function UserCard({ name, age, email, isActive }: UserCardProps) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Alter: {age}</p>
      <p>Email: {email}</p>
      <span>{isActive ? "Aktiv" : "Inaktiv"}</span>
    </div>
  );
}

Diese Schreibweise ist nicht nur sauberer und lesbarer, sondern macht auch sofort klar, welche Props die Komponente tatsächlich verwendet. Sie ist in der React-Community zum Standard geworden.

6.5 Optionale Props und Standardwerte

Nicht alle Props müssen immer übergeben werden. Sie können Props als optional markieren und Standardwerte definieren:

interface ButtonProps {
  text: string;
  type?: 'primary' | 'secondary' | 'danger';  // Optional
  disabled?: boolean;                          // Optional
}

function Button({ text, type = 'primary', disabled = false }: ButtonProps) {
  return (
    <button 
      className={`btn btn-${type}`}
      disabled={disabled}
    >
      {text}
    </button>
  );
}

// Verwendung:
<Button text="Klicken" />                    // Verwendet Standardwerte
<Button text="Löschen" type="danger" />      // Überschreibt type
<Button text="Laden" disabled={true} />     // Überschreibt disabled

Das Fragezeichen nach dem Property-Namen (type?) markiert es als optional. Die Standardwerte werden direkt in der Destructuring-Syntax definiert.

6.6 Der unidirektionale Datenfluss

Ein fundamentales Prinzip von React ist der unidirektionale Datenfluss: Props fließen immer von Eltern-Komponenten zu Kind-Komponenten, niemals umgekehrt. Dieses Prinzip hat wichtige Auswirkungen:

Props sind read-only: Eine Komponente darf ihre Props niemals direkt verändern. Props sind unveränderlich aus Sicht der empfangenden Komponente.

Vorhersagbarer Datenfluss: Da Daten nur in eine Richtung fließen, ist es einfacher zu verstehen, woher Daten kommen und wie Änderungen propagiert werden.

Einfachere Fehlersuche: Wenn ein Wert falsch ist, müssen Sie nur “nach oben” schauen, um die Quelle zu finden.

Betrachten Sie diese Hierarchie:

function App() {
  const user = { name: "Anna", role: "Admin" };
  
  return (
    <div>
      <Header user={user} />
      <MainContent user={user} />
    </div>
  );
}

function Header({ user }) {
  return <Navigation userName={user.name} userRole={user.role} />;
}

function Navigation({ userName, userRole }) {
  return (
    <nav>
      <span>Willkommen, {userName}</span>
      {userRole === "Admin" && <AdminPanel />}
    </nav>
  );
}

Die Daten fließen von App über Header zu Navigation. Jede Komponente kann entscheiden, welche Props sie an ihre Kinder weitergibt.

6.7 Props-Validierung und Fehlerbehandlung

In professionellen Anwendungen ist es wichtig, mit unerwarteten oder fehlenden Props umzugehen:

interface ProductProps {
  name: string;
  price?: number;
  description?: string;
}

function Product({ name, price, description }: ProductProps) {
  // Defensive Programmierung: Was passiert, wenn Props fehlen?
  if (!name) {
    return <div>Fehler: Produktname ist erforderlich</div>;
  }

  return (
    <div>
      <h3>{name}</h3>
      {price && <p>Preis: €{price.toFixed(2)}</p>}
      {description && <p>{description}</p>}
    </div>
  );
}

TypeScript hilft dabei, viele Probleme zur Compile-Zeit zu erkennen, aber eine defensive Programmierung in der Komponente selbst macht Ihre Anwendung robuster.

6.8 Komplexe Props: Objekte und Arrays

Props können komplexe Datenstrukturen übertragen. Dies ist besonders nützlich für Komponenten, die strukturierte Daten anzeigen:

interface UserData {
  id: number;
  name: string;
  email: string;
  preferences: {
    theme: 'light' | 'dark';
    notifications: boolean;
  };
}

interface UserProfileProps {
  user: UserData;
  actions: string[];
}

function UserProfile({ user, actions }: UserProfileProps) {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      <p>Theme: {user.preferences.theme}</p>
      
      <div>
        <h3>Verfügbare Aktionen:</h3>
        {actions.map(action => (
          <button key={action}>{action}</button>
        ))}
      </div>
    </div>
  );
}

Beachten Sie, wie wir sowohl verschachtelte Objekte (user.preferences.theme) als auch Arrays (actions.map()) verwenden können.

6.9 Performance-Überlegungen

Obwohl Props mächtig sind, sollten Sie einige Performance-Aspekte beachten:

Vermeiden Sie inline Objekte: Jedes Mal, wenn eine Komponente rendert, werden inline definierte Objekte neu erstellt:

// Schlecht: Neues Objekt bei jedem Render
<UserCard style={{color: 'red', fontSize: '14px'}} />

// Besser: Objekt außerhalb definieren
const cardStyle = {color: 'red', fontSize: '14px'};
<UserCard style={cardStyle} />

Props-Drilling vermeiden: Wenn Sie Props durch viele Komponenten-Ebenen hindurch reichen müssen, deutet dies auf ein Architekturproblem hin. Spätere Kapitel werden Lösungen wie Context API behandeln.

6.10 Troubleshooting

Props mutieren: Props sind read-only. Versuchen Sie niemals, props.value = newValue zu schreiben.

Falsche Prop-Namen: Achten Sie auf korrekte Groß-/Kleinschreibung. userName und username sind verschiedene Props.

Vergessene geschweiften Klammern: Numerische und Boolean-Werte müssen in geschweiften Klammern stehen: <Counter value={10} />, nicht <Counter value="10" />.

TypeScript-Interfaces ignorieren: Definieren Sie immer Interfaces für Ihre Props. Es spart Zeit beim Debugging und verbessert die Code-Qualität erheblich.

6.11 Props in der Praxis: Ein vollständiges Beispiel

Schauen Sie sich an, wie Props in einer realistischen Komponente zusammenwirken:

interface ArticleProps {
  title: string;
  author: string;
  publishDate: Date;
  content: string;
  tags: string[];
  featured?: boolean;
}

function Article({ 
  title, 
  author, 
  publishDate, 
  content, 
  tags, 
  featured = false 
}: ArticleProps) {
  const formatDate = (date: Date) => {
    return date.toLocaleDateString('de-DE', {
      year: 'numeric',
      month: 'long',
      day: 'numeric'
    });
  };

  return (
    <article className={featured ? 'article featured' : 'article'}>
      <header>
        <h1>{title}</h1>
        <p>Von {author} am {formatDate(publishDate)}</p>
      </header>
      
      <main>
        <p>{content}</p>
      </main>
      
      <footer>
        <div className="tags">
          {tags.map(tag => (
            <span key={tag} className="tag">#{tag}</span>
          ))}
        </div>
      </footer>
    </article>
  );
}

Diese Komponente demonstriert viele Props-Konzepte: verschiedene Datentypen, optionale Props mit Standardwerten, Array-Verarbeitung und bedingte Darstellung.

6.12 Der Weg zu interaktiven Komponenten

Props ermöglichen es, Daten in Komponenten hineinzubekommen, aber sie sind read-only. Für interaktive Anwendungen benötigen Sie einen Weg, um Daten zu ändern und auf Benutzerinteraktionen zu reagieren.

In den kommenden Kapiteln werden wir Event-Handling betrachten, um zu verstehen, wie Komponenten auf Klicks und andere Benutzeraktionen reagieren können. Anschließend führen wir State ein, das Konzept für veränderbare Daten innerhalb von Komponenten. Props und State zusammen bilden das Fundament für jede React-Anwendung.

Zunächst aber vertiefen Sie Ihr Verständnis von Props durch praktische Übungen. Experimentieren Sie mit verschiedenen Datentypen, erstellen Sie wiederverwendbare Komponenten und beobachten Sie, wie Props den Charakter Ihrer Komponenten völlig verändern können. Props sind der Schlüssel zur Komponentenkomposition – der Kunst, große Anwendungen aus kleinen, fokussierten Bausteinen zusammenzusetzen.