Zum Inhalt springen
aviral gupta

// B1.1 · ca. 30 Min. · Einstieg

Komponenten und JSX

Nach dieser Lektion schreiben Sie React-Komponenten, die JSX zurückgeben, bauen daraus ein Tic-Tac-Toe-Brett und zeigen JavaScript-Werte mit geschweiften Klammern.

Lektion 1 von 5 in B1 Schnellstart

Anfang des Moduls

Danach können Sie

  • Eine Komponente als großgeschriebene Funktion schreiben, die JSX zurückgibt, und sie in eine andere setzen
  • Die JSX-Regeln anwenden: ein Wurzelelement oder ein Fragment <>…</>, geschlossene Tags, className
  • Daten mit geschweiften Klammern zeigen und die Komponente, die der Runner rendert, mit export default exportieren
  1. Aufwärmen · Aufgabe 1 von 7

    Zum Aufwärmen aus dem JavaScript-Kurs: eine Funktion, ein Objekt und ein Template-Literal. Was gibt dieser Code aus?

    const user = { name: 'Ada', role: 'engineer' };
    
    function intro(person) {
      return `${person.name} is an ${person.role}`;
    }
    
    console.log(intro(user));
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie voraus, bevor Sie weiterlesen. Das ist eine React-Komponente: eine Funktion, die JSX zurückgibt. Welchen Text zeigt sie auf der Seite?

    function Sum() {
      return <p>2 + 2 = {2 + 2}</p>;
    }
  3. Üben · Aufgabe 3 von 7

    Ergänzen Sie das Attribut, das dem Button in JSX die CSS-Klasse square gibt, ohne Warnung von React.

    <button ____="square">X</button>
    <button ="square">X</button>
  4. Üben · Aufgabe 4 von 7

    Ordnen Sie jedem Stück JSX zu, was es bewirkt.

  5. Üben · Aufgabe 5 von 7

    Wie viele <button>-Elemente setzt Board auf die Seite?

    function Square() {
      return <button className="square">X</button>;
    }
    
    function Board() {
      return (
        <>
          <div className="board-row">
            <Square />
            <Square />
            <Square />
          </div>
          <div className="board-row">
            <Square />
            <Square />
            <Square />
          </div>
        </>
      );
    }
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Jemand hat die Komponente kleingeschrieben. Wie viele Buttons erscheinen?

    function square() {
      return <button className="square">X</button>;
    }
    
    function Board() {
      return (
        <div className="board-row">
          <square />
          <square />
        </div>
      );
    }
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe: eine Seite für einen Tic-Tac-Toe-Club. Legen Sie in main.jsx den Namen des Clubs und sein Gründungsjahr in einem Objekt site ab. Schreiben Sie eine Komponente Header, die den Namen in einem <h1> mit der Klasse title zeigt, und eine Komponente Footer, die Namen und Jahr in einem <p> zeigt, beide mit geschweiften Klammern. Der Default-Export App gibt ein Fragment mit Header, einem eigenen Absatz und Footer zurück. Führen Sie es aus und prüfen Sie, dass keine Warnung erscheint.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Ein Tic-Tac-Toe-Brett aus Square-Komponenten

Das Tutorial auf react.dev baut sein Brett aus kleinen Teilen, und dieses Beispiel auch. Square gibt einen Button zurück. Board, der Default-Export, setzt neun Felder in drei Reihen und darüber eine Überschrift und eine Statuszeile, beide mit geschweiften Klammern aus dem Objekt game gelesen. Ein Fragment ist die einzige Wurzel, die Reihen liegen also direkt in #root. styles.css gibt den Feldern ihre Größe; der Runner wendet .css-Dateien selbst auf die Seite an.

main.jsx

const game = {
  title: 'Tic-tac-toe',
  nextPlayer: 'X'
};

function Square() {
  return <button className="square">{game.nextPlayer}</button>;
}

export default function Board() {
  return (
    <>
      <h1>{game.title}</h1>
      <p className="status">Next player: {game.nextPlayer}</p>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
      <div className="board-row">
        <Square />
        <Square />
        <Square />
      </div>
    </>
  );
}

styles.css

.board-row {
  display: flex;
}

.square {
  width: 2.5rem;
  height: 2.5rem;
  margin: 0 -1px -1px 0;
  border: 1px solid #999;
  background: #fff;
  font-size: 1.5rem;
  font-weight: bold;
}

Ausführen mit

npm run dev

Ausgabe

<!-- #root -->
<div id="root"><h1>Tic-tac-toe</h1><p class="status">Next player: X</p><div class="board-row"><button class="square">X</button><button class="square">X</button><button class="square">X</button></div><div class="board-row"><button class="square">X</button><button class="square">X</button><button class="square">X</button></div><div class="board-row"><button class="square">X</button><button class="square">X</button><button class="square">X</button></div></div>
  • Das Fragment hinterlässt kein eigenes Element: Überschrift, Statuszeile und Reihen sind direkte Kinder von #root.
  • Aus className="square" wurde auf der Seite class="square".
  • Jedes {game.nextPlayer} zeigt X; Lektion B1.2 gibt jedem Feld mit Props einen eigenen Wert.
  • Vor dem Snapshot wurde nichts ausgegeben: Die Komponenten enthalten kein console.log.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.

Ihre Seite

Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.

Führen Sie das Programm aus, um seine Seite hier zu sehen.

Übungen

Übung 1 von 2

Neun Felder in drei Reihen

Bauen Sie das Brett aus dem Tutorial. Board soll neun Buttons mit der Klasse square rendern, die der Reihe nach 1 bis 9 zeigen, in drei <div>-Elementen mit der Klasse board-row, drei Buttons in jedem. Geben Sie die drei Reihen in einem Fragment zurück, damit sie direkt in #root liegen. Der Starter rendert ein Feld.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.

Ihre Seite

Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.

Führen Sie das Programm aus, um seine Seite hier zu sehen.

Hinweise
  1. Hinweis 1

    Ein zweiter <button> direkt neben dem ersten ergibt error TS2657: Die Komponente braucht eine Wurzel.

  2. Hinweis 2

    Umschließen Sie alles mit <>…</> und setzen Sie drei <div className="board-row"> hinein.

  3. Hinweis 3

    Jede Reihe enthält drei <button className="square">: 1 2 3, dann 4 5 6, dann 7 8 9.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export default function Board() {
  return (
    <>
      <div className="board-row">
        <button className="square">1</button>
        <button className="square">2</button>
        <button className="square">3</button>
      </div>
      <div className="board-row">
        <button className="square">4</button>
        <button className="square">5</button>
        <button className="square">6</button>
      </div>
      <div className="board-row">
        <button className="square">7</button>
        <button className="square">8</button>
        <button className="square">9</button>
      </div>
    </>
  );
}
Auf dem eigenen Computer ausführen

Installieren Sie React 19.3 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.jsx

export default function Board() {
  // TODO: three rows of three squares, numbered 1 to 9.
  return <button className="square">1</button>;
}

main.test.jsx

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {render, screen, within} from '@testing-library/react';
import Board from './main.jsx';

test('Das Brett zeigt neun Felder mit den Zahlen 1 bis 9', () => {
  render(<Board />);
  const labels = screen.getAllByRole('button').map((button) => button.textContent);
  assert.deepEqual(labels, ['1', '2', '3', '4', '5', '6', '7', '8', '9'], 'die Felder zeigen ' + JSON.stringify(labels));
});

test('Die Felder stehen in drei Reihen zu je drei', () => {
  const {container} = render(<Board />);
  const rows = [...container.children];
  assert.equal(rows.length, 3, 'das Brett legt ' + rows.length + ' Element(e) in #root, nicht 3 Reihen');
  rows.forEach((row, i) => {
    const count = within(row).queryAllByRole('button').length;
    assert.equal(count, 3, 'Reihe ' + (i + 1) + ' enthält ' + count + ' Feld(er)');
  });
});

test('Reihen und Felder haben ihre CSS-Klassen', () => {
  const {container} = render(<Board />);
  const rows = container.querySelectorAll('div.board-row').length;
  const squares = container.querySelectorAll('button.square').length;
  assert.equal(rows, 3, rows + ' Element(e) haben die Klasse board-row');
  assert.equal(squares, 9, squares + ' Button(s) haben die Klasse square');
});

test('React gibt keine Warnungen aus', () => {
  const warnings = [];
  const original = console.error;
  console.error = (...args) => warnings.push(args.join(' '));
  try {
    render(<Board />);
  } finally {
    console.error = original;
  }
  assert.deepEqual(warnings, [], 'console.error beim Rendern');
});

styles.css

.board-row {
  display: flex;
}

.square {
  width: 2.5rem;
  height: 2.5rem;
  margin: 0 -1px -1px 0;
  border: 1px solid #999;
  background: #fff;
  font-size: 1.5rem;
  font-weight: bold;
}

Legen Sie main.jsx in einem Vite-React-Projekt (npm create vite@latest, Vorlage react) als App.jsx in src/ ab und starten Sie es mit npm run dev. React rendert es dort wie hier in <StrictMode>.

Programm ausführen:

npm run dev

Für die Prüfungen gibt es noch keinen Befehl auf Ihrem Computer. Sie stehen in main.test.jsx, geschrieben mit node:test und React Testing Library.

Übung 2 von 2

Eine Profilkarte

Profile zeigt das Objekt user. Avatar rendert ein <img> mit der Klasse avatar, dessen src aus user.imageUrl kommt und dessen Alt-Text "Photo of " gefolgt von user.name lautet. Profile zeigt user.name im <h2> und user.role im <p>. Verwenden Sie für jeden Wert geschweifte Klammern; tippen Sie keinen Namen und keine Adresse doppelt. Das Bild lädt im Runner nicht, Sie sehen also seinen Alt-Text; die Tests prüfen die Attribute.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser React und den Compiler herunter, der JSX in JavaScript übersetzt (bis zu 2 MB), und speichert beides im Cache. Ihre Komponenten laufen in Ihrem Browser und bleiben auf Ihrem Gerät.

Ihre Seite

Ihre Seite läuft in einem abgeschotteten Rahmen. Sie hat keinen Zugriff auf diese Website, und ihr localStorage liegt nur für diesen Lauf im Speicher: Der nächste Lauf beginnt leer.

Führen Sie das Programm aus, um seine Seite hier zu sehen.

Hinweise
  1. Hinweis 1

    Führen Sie es zuerst aus: React warnt wegen class, und das src des Bildes ist der Text user.imageUrl.

  2. Hinweis 2

    Verwenden Sie className und schreiben Sie src={user.imageUrl} mit Klammern statt Anführungszeichen.

  3. Hinweis 3

    Für den Alt-Text verbinden Sie zwei Strings in Klammern: alt={'Photo of ' + user.name}, oder nehmen Sie ein Template-Literal.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

const user = {
  name: 'Hedy Lamarr',
  role: 'Inventor and actor',
  imageUrl: 'hedy-lamarr.jpg'
};

function Avatar() {
  return <img className="avatar" src={user.imageUrl} alt={'Photo of ' + user.name} />;
}

export default function Profile() {
  return (
    <article className="profile">
      <Avatar />
      <h2>{user.name}</h2>
      <p>{user.role}</p>
    </article>
  );
}
Auf dem eigenen Computer ausführen

Installieren Sie React 19.3 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.jsx

const user = {
  name: 'Hedy Lamarr',
  role: 'Inventor and actor',
  imageUrl: 'hedy-lamarr.jpg'
};

function Avatar() {
  // TODO: use className, read the address from user.imageUrl, and build the alt text from user.name.
  return <img class="avatar" src="user.imageUrl" alt="Photo" />;
}

export default function Profile() {
  // TODO: show the name and the role from user.
  return (
    <article className="profile">
      <Avatar />
      <h2>Name</h2>
      <p>Role</p>
    </article>
  );
}

main.test.jsx

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {render, screen} from '@testing-library/react';
import Profile from './main.jsx';

test('Die Überschrift zeigt den Namen aus user', () => {
  render(<Profile />);
  const heading = screen.getByRole('heading', {level: 2}).textContent;
  assert.equal(heading, 'Hedy Lamarr', 'die Überschrift lautet ' + JSON.stringify(heading));
});

test('Die Rolle aus user wird angezeigt', () => {
  render(<Profile />);
  assert.ok(screen.queryByText('Inventor and actor'), 'kein Element zeigt den Text Inventor and actor');
});

test('Das Bild liest seine Adresse aus user.imageUrl', () => {
  render(<Profile />);
  const src = screen.getByRole('img').getAttribute('src');
  assert.equal(src, 'hedy-lamarr.jpg', 'das Bild hat src=' + JSON.stringify(src));
});

test('Der Alt-Text lautet Photo of Hedy Lamarr', () => {
  render(<Profile />);
  const alt = screen.getByRole('img').getAttribute('alt');
  assert.equal(alt, 'Photo of Hedy Lamarr', 'das Bild hat alt=' + JSON.stringify(alt));
});

test('Das Bild hat die CSS-Klasse avatar', () => {
  render(<Profile />);
  const className = screen.getByRole('img').className;
  assert.equal(className, 'avatar', 'das Bild hat die Klasse ' + JSON.stringify(className));
});

test('React gibt keine Warnungen aus', () => {
  const warnings = [];
  const original = console.error;
  console.error = (...args) => warnings.push(args.join(' '));
  try {
    render(<Profile />);
  } finally {
    console.error = original;
  }
  assert.deepEqual(warnings, [], 'console.error beim Rendern');
});

styles.css

.profile {
  font-family: sans-serif;
}

.avatar {
  width: 90px;
  height: 90px;
  border-radius: 50%;
}

Legen Sie main.jsx in einem Vite-React-Projekt (npm create vite@latest, Vorlage react) als App.jsx in src/ ab und starten Sie es mit npm run dev. React rendert es dort wie hier in <StrictMode>.

Programm ausführen:

npm run dev

Für die Prüfungen gibt es noch keinen Befehl auf Ihrem Computer. Sie stehen in main.test.jsx, geschrieben mit node:test und React Testing Library.

Häufige Fehler

Zwei Elemente nebeneinander zurückgeben

export default function Board() {
  return (
    <h1>Tic-tac-toe</h1>
    <div className="board-row">
      <button className="square">1</button>
    </div>
  );
}

Was React ausgibt

error TS2657: JSX expressions must have one parent element.

Warum, und die Lösung

Eine Komponente gibt genau ein Wurzelelement zurück, deshalb läuft nichts: Die JSX-Umwandlung bricht mit diesem Fehler ab. Packen Sie die Geschwister in ein Elternelement wie ein <div> oder in ein Fragment, <>…</>, das der Seite kein Element hinzufügt. Der Runner nutzt die Umwandlung von TypeScript; die Sandbox auf react.dev meldet denselben Fehler als "Adjacent JSX elements must be wrapped in an enclosing tag."

Eine Komponente mit kleingeschriebenem Namen

function square() {
  return <button className="square">X</button>;
}

export default function Board() {
  return (
    <div className="board-row">
      <square />
      <square />
    </div>
  );
}

Was React ausgibt

The tag <square> is unrecognized in this browser.

Warum, und die Lösung

JSX behandelt ein kleingeschriebenes Tag als HTML, <square /> erzeugt also ein unbekanntes <square>-Element und ruft Ihre Funktion nie auf. Die Warnung geht weiter: "If you meant to render a React component, start its name with an uppercase letter." Nennen Sie die Funktion Square und schreiben Sie <Square />.

class statt className schreiben

export default function Square() {
  return <button class="square">X</button>;
}

Was React ausgibt

Invalid DOM property `class`. Did you mean `className`?

Warum, und die Lösung

In JSX heißt die CSS-Klasse className. React 19 setzt class="square" trotzdem auf den Button, die Seite sieht also vielleicht sogar richtig aus, aber React warnt in der Konsole, und der Runner des Kurses wertet jede Warnung als fehlgeschlagenen Lauf. Schreiben Sie <button className="square">.

React im Browser: React 19.3.0 (Entwicklungs-Build) und React Testing Library, MIT, von dieser Website ausgeliefert; JSX übersetzt TypeScript 6.0.3, Apache-2.0. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Komponenten sind Funktionen, die JSX zurückgeben

Eine React-Komponente ist eine JavaScript-Funktion, die Markup zurückgibt, etwa function Square() { return <button className="square">X</button>; }. Das Markup ist JSX: Es sieht aus wie HTML, steht aber mitten in JavaScript. Sobald Square existiert, verwendet eine andere Komponente sie wie ein Tag, <Square />, so oft sie will: Ein Board kann neun davon enthalten. Der Name muss mit einem Großbuchstaben beginnen. Daran unterscheidet JSX Ihre Komponente von einem HTML-Tag: <Square /> ruft Ihre Funktion auf, <square /> dagegen wäre ein unbekanntes HTML-Element, und React warnt davor in der Konsole.

JSX ist strenger als HTML

Drei Regeln bringen die meisten Einsteiger ins Stolpern. Erstens gibt eine Komponente genau ein Wurzelelement zurück. Zwei Geschwister nebeneinander, etwa ein <h1> neben einem <div>, lassen sich nicht übersetzen; packen Sie sie in ein Elternelement wie ein <div> oder in ein Fragment, <>…</>, das sie gruppiert, ohne der Seite ein Element hinzuzufügen. Zweitens wird jedes Tag geschlossen: <br /> und <img … /> enden mit einem Schrägstrich, und <button> braucht sein </button>. Drittens heißt die CSS-Klasse className, denn class ist in JavaScript ein reserviertes Wort. React rendert class trotzdem, warnt aber.

Geschweifte Klammern und export default

Geschweifte Klammern wechseln von JSX zurück zu JavaScript. <h1>{game.title}</h1> zeigt den Wert von game.title, und jeder Ausdruck funktioniert: {2 + 2}, {user.score * 2} oder ein Template-Literal. In einem Attribut stehen Klammern statt Anführungszeichen: src={user.imageUrl} übergibt den Wert, src="user.imageUrl" dagegen die Wörter user.imageUrl als bloßen Text. Schließlich markiert export default die Hauptkomponente einer Datei. Der Runner des Kurses rendert den Default-Export von main.jsx in das leere #root der Seite, im Strict Mode von React, genau wie ein Vite-Projekt App rendert.

Quellen

Zuletzt geprüft am 5. Oktober 2026