Zum Inhalt springen
aviral gupta

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

Ihre erste Komponente, Import und Export

Nach dieser Lektion definieren und verschachteln Sie Komponenten wie auf react.dev und verteilen sie mit Default- und benannten Exporten auf Dateien.

Lektion 1 von 6 in B2 Die Oberfläche beschreiben

Anfang des Moduls

Danach können Sie

  • Eine Komponente mit export default, einer großgeschriebenen Funktion und einem return mit Klammern für mehrzeiliges JSX definieren
  • Komponenten in einer Elternkomponente verschachteln und jede Definition auf oberster Ebene halten
  • Komponenten auf Dateien verteilen und Default- und benannte Exporte jeweils passend importieren
  1. Aufwärmen · Aufgabe 1 von 7

    Zum Aufwärmen aus Modul B1: eine Komponente, eine Prop und geschweifte Klammern. Welchen Text zeigt Page?

    function Greeting({ name }) {
      return <p>Hello, {name}</p>;
    }
    
    function Page() {
      return <Greeting name="Ada" />;
    }
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie voraus, bevor Sie weiterlesen. Das JSX beginnt in der Zeile nach return. Welches HTML setzt Gallery in #root?

    function Profile() {
      return
        <img src="ada.jpg" alt="Ada" />;
    }
    
    function Gallery() {
      return <section><Profile /></section>;
    }
  3. Üben · Aufgabe 3 von 7

    Ergänzen Sie das Schlüsselwort, das Gallery zum Hauptexport seiner Datei macht.

    export ____ function Gallery() {
      return <h1>Gallery</h1>;
    }
  4. Üben · Aufgabe 4 von 7

    Ordnen Sie jeder Zeile in Button.jsx oder main.jsx zu, was sie bedeutet.

  5. Üben · Aufgabe 5 von 7

    Gallery.jsx liegt neben main.jsx. Mit welcher Zeile in main.jsx bekommen Sie die Komponente Profile?

    // Gallery.jsx
    export default function Gallery() {
      return <section>…</section>;
    }
    
    export function Profile() {
      return <img src="ada.jpg" alt="Ada Lovelace" />;
    }
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Counter ist innerhalb von Page definiert. Sie klicken dreimal auf den Zähler, dann auf Theme. Was zeigt der Zähler jetzt?

    function Page() {
      const [dark, setDark] = useState(false);
    
      function Counter() {
        const [count, setCount] = useState(0);
        return <button onClick={() => setCount(count + 1)}>{count}</button>;
      }
    
      return (
        <>
          <Counter />
          <button onClick={() => setDark(!dark)}>Theme</button>
        </>
      );
    }
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe: eine Seite für einen Buchclub, in Komponenten gegliedert. Schreiben Sie in main.jsx Header (ein <h1> mit dem Namen des Clubs), Book (ein <li> mit einem Coverbild und einer Textzeile) und Shelf (eine <ul>, die Book dreimal rendert). Der Default-Export App rendert Header und Shelf. Definieren Sie jede Komponente auf oberster Ebene. Führen Sie es aus und prüfen Sie, dass #root eine Überschrift und drei Listeneinträge enthält. Wenn Sie mit Vite arbeiten, verschieben Sie Book probeweise nach Book.jsx und importieren Sie es.

    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

Eine Galerie, auf drei Dateien verteilt

Die Galerie von react.dev, auf Dateien verteilt. Profile.jsx exportiert Profile, ein Bild, als Default. Gallery.jsx importiert es und rendert es dreimal; die Datei exportiert Gallery als Default und dazu den benannten Export Heading. main.jsx importiert alle drei: die Default-Exporte ohne Klammern, Heading in Klammern, jeden Pfad mit Endung. Ihr eigener Default-Export App ist das, was der Runner in #root rendert. Die Bilder laden im Runner nicht; der Snapshot zeigt das HTML.

main.jsx

import Gallery from './Gallery.jsx';
import {Heading} from './Gallery.jsx';
import Profile from './Profile.jsx';

export default function App() {
  return (
    <>
      <Heading />
      <Gallery />
      <h2>Scientist of the month</h2>
      <Profile />
    </>
  );
}

Gallery.jsx

import Profile from './Profile.jsx';

export function Heading() {
  return <h1>Amazing scientists</h1>;
}

export default function Gallery() {
  return (
    <section>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

Profile.jsx

export default function Profile() {
  return (
    <img
      src="katherine-johnson.jpg"
      alt="Katherine Johnson"
    />
  );
}

Ausführen mit

npm run dev

Ausgabe

<!-- #root -->
<div id="root"><h1>Amazing scientists</h1><section><img alt="Katherine Johnson" src="katherine-johnson.jpg"><img alt="Katherine Johnson" src="katherine-johnson.jpg"><img alt="Katherine Johnson" src="katherine-johnson.jpg"></section><h2>Scientist of the month</h2><img alt="Katherine Johnson" src="katherine-johnson.jpg"></div>
  • Profile.jsx wird zweimal importiert, von Gallery.jsx und von main.jsx; beide bekommen dieselbe Komponente.
  • Gallery.jsx hat einen Default-Export, Gallery, und einen benannten Export, Heading. main.jsx importiert sie in zwei Zeilen, wie react.dev.
  • Kein Dateiname erscheint im HTML, nur das, was die Komponenten zurückgeben. React hat alt vor src geschrieben; die Reihenfolge der Attribute spielt keine Rolle.
  • Lokal in Vite liegen diese Dateien in src/ neben App.jsx.
Ä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

Die Aufteilung in Dateien abschließen

TeamPage hat früher alles in main.jsx definiert. Title, Avatar und Footer wurden in Dateien verschoben: Avatar.jsx exportiert Avatar als Default, und Labels.jsx hat die benannten Exporte Title und Footer. Schließen Sie die Aufteilung ab: Löschen Sie die alten Kopien aus main.jsx, importieren Sie die Komponenten passend aus den Dateien und rendern Sie weiterhin Title, Avatar und Footer in <main>, in dieser Reihenfolge.

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

    Öffnen Sie Avatar.jsx und Labels.jsx: Ist jede Komponente mit default exportiert oder ohne?

  2. Hinweis 2

    Ein Default-Export wird ohne Klammern importiert: import Avatar from './Avatar.jsx';

  3. Hinweis 3

    Zwei benannte Exporte aus einer Datei stehen in einem Klammerpaar: import {Title, Footer} from './Labels.jsx';

Eine Lösung zeigen

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

import Avatar from './Avatar.jsx';
import {Title, Footer} from './Labels.jsx';

export default function TeamPage() {
  return (
    <main>
      <Title />
      <Avatar />
      <Footer />
    </main>
  );
}
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

// The components now live in Avatar.jsx and Labels.jsx. Finish the split:
// delete these old copies and import the new ones instead.
function Title() {
  return <h1>Team</h1>;
}

function Avatar() {
  return <img className="avatar" src="ada.jpg" alt="Ada" />;
}

function Footer() {
  return <p>Made by us</p>;
}

export default function TeamPage() {
  return (
    <main>
      <Title />
      <Avatar />
      <Footer />
    </main>
  );
}

main.test.jsx

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

test('Die Überschrift kommt von Title aus Labels.jsx', () => {
  render(<TeamPage />);
  const heading = screen.getByRole('heading', {level: 1}).textContent;
  assert.equal(heading, 'Our team', 'die Überschrift lautet ' + JSON.stringify(heading));
});

test('Das Bild kommt von Avatar aus Avatar.jsx', () => {
  render(<TeamPage />);
  const alt = screen.getByRole('img').getAttribute('alt');
  assert.equal(alt, 'Ada Lovelace', 'das Bild hat alt=' + JSON.stringify(alt));
});

test('Die Fußzeile kommt von Footer aus Labels.jsx', () => {
  render(<TeamPage />);
  assert.ok(screen.queryByText('Made with React'), 'kein Element zeigt den Text Made with React');
});

test('Title, Avatar und Footer stehen in dieser Reihenfolge in <main>', () => {
  render(<TeamPage />);
  const tags = [...screen.getByRole('main').children].map((child) => child.tagName.toLowerCase());
  assert.deepEqual(tags, ['h1', 'img', 'p'], '<main> enthält ' + JSON.stringify(tags));
});

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

Avatar.jsx

export default function Avatar() {
  return <img className="avatar" src="ada-lovelace.jpg" alt="Ada Lovelace" />;
}

Labels.jsx

export function Title() {
  return <h1>Our team</h1>;
}

export function Footer() {
  return <p>Made with React</p>;
}

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

Drei Fotos mit eigenen Likes

Gallery zeigt ein Foto mit einem LikeButton, und LikeButton ist innerhalb von Gallery definiert. Klicken Sie auf Like, dann auf Dark mode: Die Likes sind weg. Verschieben Sie LikeButton auf die oberste Ebene der Datei und zeigen Sie drei Fotos (Sunrise, Forest und Harbour, jedes ein <img> mit diesem Alt-Text), jedes mit einem eigenen <LikeButton />. Der Button Dark mode bleibt vorn.

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 den Starter aus, klicken Sie zweimal auf Like, dann auf Dark mode, und beobachten Sie den Zähler.

  2. Hinweis 2

    Schneiden Sie die ganze function LikeButton() { … } aus und fügen Sie sie über export default function Gallery() ein.

  3. Hinweis 3

    Jedes Foto ist eine <figure> mit einem <img> und einem <LikeButton />; schreiben Sie drei davon.

Eine Lösung zeigen

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

import {useState} from 'react';

function LikeButton() {
  const [likes, setLikes] = useState(0);
  return <button onClick={() => setLikes(likes + 1)}>Like ({likes})</button>;
}

export default function Gallery() {
  const [dark, setDark] = useState(false);
  return (
    <section className={dark ? 'dark' : 'light'}>
      <button onClick={() => setDark(!dark)}>Dark mode</button>
      <figure>
        <img src="sunrise.jpg" alt="Sunrise" />
        <LikeButton />
      </figure>
      <figure>
        <img src="forest.jpg" alt="Forest" />
        <LikeButton />
      </figure>
      <figure>
        <img src="harbour.jpg" alt="Harbour" />
        <LikeButton />
      </figure>
    </section>
  );
}
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

import {useState} from 'react';

export default function Gallery() {
  const [dark, setDark] = useState(false);

  // TODO: move LikeButton out of Gallery, to the top level of the file.
  function LikeButton() {
    const [likes, setLikes] = useState(0);
    return <button onClick={() => setLikes(likes + 1)}>Like ({likes})</button>;
  }

  // TODO: show three photos, each with its own LikeButton.
  return (
    <section className={dark ? 'dark' : 'light'}>
      <button onClick={() => setDark(!dark)}>Dark mode</button>
      <figure>
        <img src="sunrise.jpg" alt="Sunrise" />
        <LikeButton />
      </figure>
    </section>
  );
}

main.test.jsx

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

const likes = () => screen.getAllByRole('button', {name: /^Like/}).map((button) => button.textContent);

test('Drei Fotos, jedes mit einem Like-Button', () => {
  render(<Gallery />);
  const photos = screen.getAllByRole('img').length;
  assert.equal(photos, 3, 'die Galerie zeigt ' + photos + ' Foto(s)');
  assert.deepEqual(likes(), ['Like (0)', 'Like (0)', 'Like (0)'], 'die Like-Buttons zeigen ' + JSON.stringify(likes()));
});

test('Jeder Like-Button zählt für sich', async () => {
  const user = userEvent.setup();
  render(<Gallery />);
  const first = screen.getAllByRole('button', {name: /^Like/})[0];
  await user.click(first);
  await user.click(first);
  assert.deepEqual(likes(), ['Like (2)', 'Like (0)', 'Like (0)'], 'nach zwei Klicks auf den ersten: ' + JSON.stringify(likes()));
});

test('Die Likes überstehen einen Klick auf Dark mode', async () => {
  const user = userEvent.setup();
  render(<Gallery />);
  await user.click(screen.getAllByRole('button', {name: /^Like/})[0]);
  await user.click(screen.getByRole('button', {name: 'Dark mode'}));
  assert.equal(likes()[0], 'Like (1)', 'nach einem Like und Dark mode zeigt der erste Button ' + JSON.stringify(likes()[0]));
});

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

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

Einen benannten Export als Default importieren

import Gallery from './Gallery.jsx';

export default function App() {
  return <Gallery />;
}

Was React ausgibt

does not provide an export named 'default'

Warum, und die Lösung

Im Browser lautet die ganze Zeile SyntaxError: The requested module './Gallery.jsx' does not provide an export named 'default', und nichts läuft. Gallery.jsx exportiert Gallery mit Namen, importieren Sie es also in Klammern: import {Gallery} from './Gallery.jsx'. Oder machen Sie es mit export default function Gallery() zum Default der Datei. Der Wortlaut ist der von V8 (Chrome, Node); Firefox und Safari formulieren anders.

Einen Default-Export in Klammern importieren

import {Gallery} from './Gallery.jsx';

export default function App() {
  return <Gallery />;
}

Was React ausgibt

does not provide an export named 'Gallery'

Warum, und die Lösung

Klammern verlangen einen Export mit genau diesem Namen, aber Gallery.jsx hat nur einen Default-Export. Die ganze Zeile ist ein SyntaxError, der das Modul './Gallery.jsx' nennt. Lassen Sie die Klammern weg: import Gallery from './Gallery.jsx'. react.dev: Sie bekommen einen Fehler, wenn Sie einen Default-Export so importieren wie einen benannten. Der Wortlaut ist der von V8; Firefox und Safari formulieren anders.

Zwei Default-Exporte in einer Datei

import Gallery from './Gallery.jsx';

export default function App() {
  return <Gallery />;
}

Was React ausgibt

SyntaxError: Duplicate export of 'default'

Warum, und die Lösung

Eine Datei kann nur einen Default-Export haben, Gallery.jsx lädt also nicht, und nichts läuft. Lassen Sie export default bei der Hauptkomponente Gallery und machen Sie Profile zu einem benannten Export, export function Profile(), oder lassen Sie den Export weg, wenn nur Gallery es verwendet. Der Wortlaut ist der von V8; Firefox und Safari formulieren anders.

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

Eine Komponente ist eine Funktion, die Markup zurückgibt

react.dev baut eine Komponente in drei Schritten. Exportieren: export default markiert die Hauptfunktion einer Datei, und das ist gewöhnliches JavaScript, nicht React. Definieren: function Profile() { }, mit Großbuchstaben. Markup hinzufügen: das JSX zurückgeben. Ein kurzes return passt in eine Zeile. Beginnt das Markup erst in der nächsten Zeile, setzen Sie es in Klammern, return ( … ). Ohne sie beendet JavaScript die Anweisung nach return: Die Komponente gibt nichts zurück und rendert nichts, ohne dass ein Fehler Sie warnt.

Komponenten verschachteln, aber jede auf oberster Ebene definieren

Sobald Profile existiert, kann Gallery <Profile /> so oft rendern, wie es will. Gallery ist dann die Elternkomponente und jedes Profile ein Kind. Viele kleine Komponenten dürfen sich eine Datei teilen. Definieren Sie aber nie eine Komponente innerhalb einer anderen. Laut react.dev ist solcher Code sehr langsam und verursacht Fehler: Bei jedem Rendern von Gallery entsteht eine neue Funktion Profile, React sieht an derselben Stelle eine andere Komponente und setzt ihren State zurück. Definieren Sie jede Komponente auf oberster Ebene der Datei und geben Sie Daten mit Props weiter.

Default- und benannte Exporte

Eine Datei hat höchstens einen Default-Export und beliebig viele benannte. Importieren Sie jeden so, wie er exportiert wurde: export default function Gallery() importieren Sie mit import Gallery from './Gallery.jsx', und den Namen dürfen Sie frei wählen. export function Profile() importieren Sie mit import {Profile} from './Gallery.jsx', und der Name muss übereinstimmen. Wer die beiden Formen vertauscht, bekommt einen Fehler. Im Runner des Kurses liegen Dateien neben main.jsx und werden mit Endung importiert; react.dev merkt an, dass './Gallery' ohne Endung mit React ebenfalls funktioniert.

Quellen

Zuletzt geprüft am 5. Oktober 2026