Zum Inhalt springen
aviral gupta

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

Installation: ein Framework oder von Grund auf

Nach dieser Lektion kennen Sie die Wege zu React, die react.dev heute nennt, wählen einen für ein Projekt und fügen React in einen Teil einer bestehenden Seite ein.

Lektion 1 von 4 in B4 Ein Projekt einrichten

Anfang des Moduls

Danach können Sie

  • React ohne Installation ausprobieren und sagen, was der Runner dieses Kurses wie ein Vite-Projekt tut
  • Zwischen einem Framework und einem Build-Tool von Grund auf wählen und die Optionen von react.dev nennen
  • React mit createRoot in einen Teil einer bestehenden Seite einfügen, eine Root pro UI-Teil
  1. Aufwärmen · Aufgabe 1 von 7

    Zum Aufwärmen aus Lektion B3.6: die Updater-Funktionen des Warenkorbs. Sie klicken einmal auf den Button. Was zeigt er?

    function AddTwice() {
      const [quantity, setQuantity] = useState(0);
    
      function handleClick() {
        setQuantity((q) => q + 1);
        setQuantity((q) => q + 1);
      }
    
      return <button onClick={handleClick}>Quantity: {quantity}</button>;
    }
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie voraus, bevor Sie weiterlesen. Der <body> einer bestehenden Seite enthält <p>Welcome to the shop</p><nav id="navigation"></nav>. Ihr Skript führt den Code unten aus. Welche Elemente enthält <body> danach? Jedes Element ist aufgeführt, sein erstes Kind nach >.

    function NavigationBar() {
      return <h1>Hello from React!</h1>;
    }
    
    const domNode = document.getElementById('navigation');
    const navRoot = createRoot(domNode);
    navRoot.render(<NavigationBar />);
  3. Üben · Aufgabe 3 von 7

    Sie wollen eine neue React-App mit eingebautem Routing und nutzen React Router als Framework. Welchen Befehl zeigt react.dev dafür?

  4. Üben · Aufgabe 4 von 7

    Ordnen Sie jedem Namen von den Installationsseiten von react.dev zu, was er ist.

  5. Üben · Aufgabe 5 von 7

    Das HTML der bestehenden Seite hat <nav id="navigation"></nav>. Ergänzen Sie das Argument, sodass NavigationBar darin rendert.

    // The page's HTML: <nav id="navigation"></nav>
    const domNode = document.getElementById('____');
    const navRoot = createRoot(domNode);
    navRoot.render(<NavigationBar />);
    document.getElementById('');
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Die Seite hat <div id="top"> und <div id="bottom">, und jedes bekommt eine eigene Root mit einem LikeButton. Sie klicken einmal auf den Button in #top. Was zeigen #top und #bottom?

    function LikeButton() {
      const [liked, setLiked] = useState(false);
    
      return (
        <button onClick={() => setLiked(true)}>
          {liked ? 'Liked' : 'Like'}
        </button>
      );
    }
    
    createRoot(document.getElementById('top')).render(<LikeButton />);
    createRoot(document.getElementById('bottom')).render(<LikeButton />);
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe: eine Komponente, drei Orte. Schreiben Sie Greeting und App von unten in die main.jsx dieses Kurses und führen Sie sie aus. Öffnen Sie dann die Seite Installation auf react.dev und bearbeiten Sie deren Sandbox (ihre Datei ist App.js), und, wenn Sie mögen, öffnen Sie vite.new/react, das vite.dev nennt, um eine Vite-Vorlage online auszuprobieren. Ändern Sie überall den Namen in Ihren. Notieren Sie für jeden Ort, was Sie installiert haben, welche Datei die Komponente enthält und was sie auf die Seite bringt.

    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 App in #root, ein Widget in einem <nav>

Zwei Wege, auf denen React auf eine Seite kommt. Der Default-Export App ist die ganze App: Der Runner rendert ihn in #root, wie die src/main.jsx eines Vite-Projekts. Darunter verhält sich main.jsx wie ein Skript auf einer bestehenden Seite: Es findet <nav id="navigation"> und gibt ihm eine eigene Root für NavigationBar, wie auf der Seite von react.dev zum Einfügen von React in ein bestehendes Projekt. Die Seite des Runners hat nur #root, also fügt der Code zuerst das <nav> ein, das eine bestehende Seite schon hätte. Der Schnappschuss zeigt den ganzen <body>.

main.jsx

import { createRoot } from 'react-dom/client';
import App from './App.jsx';
import NavigationBar from './NavigationBar.jsx';

// 1. The app. The runner renders this default export into #root inside
//    <StrictMode>, as the src/main.jsx of a Vite project does with
//    createRoot(document.getElementById('root')).render(<App />).
export default App;

// 2. A widget on an existing page. Its HTML would already contain
//    <nav id="navigation"></nav>; the runner's page has only #root, so add it.
const nav = document.createElement('nav');
nav.id = 'navigation';
document.body.prepend(nav);

const domNode = document.getElementById('navigation');
const navRoot = createRoot(domNode);
navRoot.render(<NavigationBar />);

App.jsx

export default function App() {
  return (
    <main>
      <h1>Bookshop</h1>
      <p>The whole app lives in #root.</p>
    </main>
  );
}

NavigationBar.jsx

export default function NavigationBar() {
  return (
    <ul>
      <li>Books</li>
      <li>Cart</li>
    </ul>
  );
}

Ausführen mit

npm run dev

Ausgabe

<!-- body -->
<body><nav id="navigation"><ul><li>Books</li><li>Cart</li></ul></nav><div id="root"><main><h1>Bookshop</h1><p>The whole app lives in #root.</p></main></div></body>
  • Zwei Roots, zwei Bäume: App in #root und NavigationBar im <nav>. Keiner weiß vom anderen.
  • Der Runner ruft wie Vites main.jsx createRoot für #root auf; der Code ruft es für das Widget noch einmal auf, auf einem anderen Element.
  • React füllt nur die Elemente, die es bekommen hat. Alles andere auf einer bestehenden Seite bleibt, wie sein HTML es gebaut hat.
  • App.jsx und NavigationBar.jsx werden mit ihrer Endung importiert, wie im Ordner src/ eines Vite-Projekts.
Ä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

Ein Like-Button auf einer bestehenden Seite

Eine Shop-Seite, die nicht mit React gebaut ist, möchte einen Like-Button in ihrem <div id="like"></div>. Vervollständigen Sie mountLikeButton(domNode): Erzeugen Sie eine Root für domNode, rendern Sie <LikeButton /> hinein und geben Sie die Root zurück, damit die Seite das Widget später mit root.unmount() entfernen kann. Ausführen zeigt Page; die Tests bauen das HTML der bestehenden Seite und rufen Ihre Funktion auf.

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

    createRoot(domNode) erzeugt eine Root für das Element; angezeigt wird nichts, bis Sie render darauf aufrufen.

  2. Hinweis 2

    Halten Sie die Root in einer Variablen: const root = createRoot(domNode);

  3. Hinweis 3

    Dann root.render(<LikeButton />); und return root;

Eine Lösung zeigen

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

import { useState } from 'react';
import { createRoot } from 'react-dom/client';

function LikeButton() {
  const [liked, setLiked] = useState(false);

  return (
    <button onClick={() => setLiked(true)}>
      {liked ? 'Liked' : 'Like'}
    </button>
  );
}

export default function Page() {
  return <h1>Product page</h1>;
}

// The existing page calls this with its <div id="like"></div>.
export function mountLikeButton(domNode) {
  const root = createRoot(domNode);
  root.render(<LikeButton />);
  return root;
}
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';
import { createRoot } from 'react-dom/client';

function LikeButton() {
  const [liked, setLiked] = useState(false);

  return (
    <button onClick={() => setLiked(true)}>
      {liked ? 'Liked' : 'Like'}
    </button>
  );
}

export default function Page() {
  return <h1>Product page</h1>;
}

// The existing page calls this with its <div id="like"></div>.
export function mountLikeButton(domNode) {
  // TODO: create a root for domNode, render <LikeButton /> into it,
  // and return the root.
}

main.test.jsx

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

// The HTML of the existing page: some text, and an empty element for the widget.
function existingPage() {
  document.body.innerHTML = '<p>Made by the shop team</p><div id="like"></div>';
  return document.getElementById('like');
}

async function mount(domNode) {
  let root;
  await act(async () => {
    root = mountLikeButton(domNode);
  });
  return root;
}

async function unmount(root) {
  if (root && typeof root.unmount === 'function') await act(async () => root.unmount());
}

test('Der Like-Button erscheint in #like', async () => {
  const domNode = existingPage();
  const root = await mount(domNode);
  const button = within(domNode).queryByRole('button', {name: 'Like'});
  assert.ok(button !== null, '#like enthält ' + JSON.stringify(domNode.innerHTML) + ', keinen Like-Button');
  await unmount(root);
});

test('Ein Klick ändert den Button zu Liked', async () => {
  const user = userEvent.setup();
  const domNode = existingPage();
  const root = await mount(domNode);
  const button = within(domNode).queryByRole('button', {name: 'Like'});
  assert.ok(button !== null, 'in #like gibt es keinen Like-Button zum Klicken');
  await user.click(button);
  assert.equal(button.textContent, 'Liked', 'nach einem Klick zeigt der Button ' + JSON.stringify(button.textContent));
  await unmount(root);
});

test('mountLikeButton gibt die Root zurück und lässt den Rest der Seite stehen', async () => {
  const domNode = existingPage();
  const root = await mount(domNode);
  assert.equal(typeof root?.unmount, 'function', 'mountLikeButton hat ' + String(root) + ' zurückgegeben, nicht die Root von createRoot');
  assert.equal(document.querySelector('p')?.textContent, 'Made by the shop team', 'der Text der Seite außerhalb von #like hat sich geändert');
  await unmount(root);
});

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

Zwei Widgets, zwei Roots

Die Shop-Seite hat <span id="cart-badge"> in ihrem Kopf und <div id="newsletter"> in ihrem Fuß. mountWidgets soll im ersten CartBadge mit der Zahl 2 zeigen und im zweiten Newsletter. Im Moment rendert es beides in dieselbe Root, also wird das Badge durch den Newsletter ersetzt, und der Fuß bleibt leer. Geben Sie jedem Element eine eigene Root und geben Sie beide Roots in einem Array zurück.

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 erneuter Aufruf von render auf derselben Root ersetzt, was sie vorher gezeigt hat.

  2. Hinweis 2

    Rufen Sie createRoot ein zweites Mal auf, mit document.getElementById('newsletter').

  3. Hinweis 3

    Geben Sie [badgeRoot, newsletterRoot] zurück, damit die Seite jedes Widget aushängen kann.

Eine Lösung zeigen

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

import { createRoot } from 'react-dom/client';

function CartBadge({ count }) {
  return <strong>Cart: {count}</strong>;
}

function Newsletter() {
  return (
    <section>
      <h2>Newsletter</h2>
      <button>Subscribe</button>
    </section>
  );
}

export default function Page() {
  return <h1>Shop</h1>;
}

// The existing page has <span id="cart-badge"></span> in its header
// and <div id="newsletter"></div> in its footer.
export function mountWidgets() {
  const badgeRoot = createRoot(document.getElementById('cart-badge'));
  badgeRoot.render(<CartBadge count={2} />);

  const newsletterRoot = createRoot(document.getElementById('newsletter'));
  newsletterRoot.render(<Newsletter />);

  return [badgeRoot, newsletterRoot];
}
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 { createRoot } from 'react-dom/client';

function CartBadge({ count }) {
  return <strong>Cart: {count}</strong>;
}

function Newsletter() {
  return (
    <section>
      <h2>Newsletter</h2>
      <button>Subscribe</button>
    </section>
  );
}

export default function Page() {
  return <h1>Shop</h1>;
}

// The existing page has <span id="cart-badge"></span> in its header
// and <div id="newsletter"></div> in its footer.
export function mountWidgets() {
  const root = createRoot(document.getElementById('cart-badge'));
  root.render(<CartBadge count={2} />);
  root.render(<Newsletter />);
  return [root];
}

main.test.jsx

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

function existingPage() {
  document.body.innerHTML =
    '<header>Shop <span id="cart-badge"></span></header><main>Books</main><footer><div id="newsletter"></div></footer>';
}

async function mount() {
  let roots;
  await act(async () => {
    roots = mountWidgets();
  });
  return Array.isArray(roots) ? roots : [];
}

async function unmountAll(roots) {
  for (const root of roots) if (root && typeof root.unmount === 'function') await act(async () => root.unmount());
}

test('Das Warenkorb-Badge zeigt Cart: 2', async () => {
  existingPage();
  const roots = await mount();
  const badge = document.getElementById('cart-badge');
  assert.equal(badge.textContent, 'Cart: 2', '#cart-badge zeigt ' + JSON.stringify(badge.textContent));
  await unmountAll(roots);
});

test('Der Newsletter erscheint in #newsletter', async () => {
  existingPage();
  const roots = await mount();
  const footer = document.getElementById('newsletter');
  const button = within(footer).queryByRole('button', {name: 'Subscribe'});
  assert.ok(button !== null, '#newsletter enthält ' + JSON.stringify(footer.innerHTML) + ', nicht den Button Subscribe');
  await unmountAll(roots);
});

test('mountWidgets gibt eine Root pro Widget zurück', async () => {
  existingPage();
  const roots = await mount();
  assert.equal(roots.length, 2, 'mountWidgets hat ' + roots.length + ' Root(s) zurückgegeben');
  assert.ok(roots[0] !== roots[1], 'beide Einträge sind dieselbe Root');
  await unmountAll(roots);
});

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

Die Komponente an createRoot übergeben

import { createRoot } from 'react-dom/client';

function NavigationBar() {
  return <p>Books and cart</p>;
}

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

const nav = document.createElement('nav');
document.body.prepend(nav);

// The order of the old ReactDOM.render(element, container), in reverse
createRoot(nav, <NavigationBar />);

Was React ausgibt

You passed a JSX element to createRoot. You probably meant to call root.render instead.

Warum, und die Lösung

createRoot nimmt nur das DOM-Element, dazu optionale Optionen; es rendert nichts. React warnt, und das <nav> bleibt leer. Behalten Sie die Root und rendern Sie in sie: const navRoot = createRoot(nav); navRoot.render(<NavigationBar />);

Das Element noch einmal an root.render übergeben

import { createRoot } from 'react-dom/client';

function NavigationBar() {
  return <p>Books and cart</p>;
}

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

const nav = document.createElement('nav');
document.body.prepend(nav);

const navRoot = createRoot(nav);
navRoot.render(<NavigationBar />, nav);

Was React ausgibt

You passed a container to the second argument of root.render(...).

Warum, und die Lösung

Die Root kennt ihr Element schon: Sie haben es createRoot übergeben. root.render nimmt ein Argument, das anzuzeigende JSX, also warnt React wegen des zweiten. Schreiben Sie navRoot.render(<NavigationBar />); alte Tutorials, die das Element zweimal übergeben, wurden für ReactDOM.render geschrieben, das React 19 entfernt hat.

render auf dem DOM-Element aufrufen

function NavigationBar() {
  return <p>Books and cart</p>;
}

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

const nav = document.createElement('nav');
nav.id = 'navigation';
document.body.prepend(nav);

document.getElementById('navigation').render(<NavigationBar />);

Was React ausgibt

TypeError: document.getElementById(...).render is not a function

Warum, und die Lösung

Ein DOM-Element hat keine Methode render; eine React-Root hat eine. Der Lauf stoppt mit Uncaught TypeError (Formulierung von Chrome und Node.js; Firefox und Safari formulieren es anders). Importieren Sie createRoot aus 'react-dom/client', erzeugen Sie eine Root für das Element und rufen Sie render auf der Root auf: createRoot(document.getElementById('navigation')).render(<NavigationBar />);

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

React ausprobieren, ohne etwas zu installieren

React wurde für schrittweise Einführung entworfen: Sie nutzen so wenig oder so viel davon, wie Sie brauchen. Zum Ausprobieren müssen Sie nichts installieren. Die Seiten von react.dev haben Sandboxes, und CodeSandbox, StackBlitz und CodePen führen React online aus. Der Runner dieses Kurses arbeitet wie ein Vite-Projekt: Die Seite ist ein leeres <div id="root">, und der Default-Export Ihrer main.jsx wird darin in <StrictMode> gerendert, so wie Vites src/main.jsx es mit createRoot tut. Ein alter Weg ist zu: Laut react.dev ist Create React App veraltet (deprecated), beginnen Sie also kein Projekt damit.

Eine neue App: mit einem Framework beginnen

Für eine neue App oder Website empfiehlt react.dev, mit einem Framework zu beginnen. Es nennt Next.js (App Router), npx create-next-app@latest; React Router (v7), npx create-react-router@latest; und Expo für native Android- und iOS-Apps, npx create-expo-app@latest. TanStack Start (Beta) und RedwoodSDK stehen als aufstrebend auf der Liste. Diese Frameworks brauchen keinen Server: Sie unterstützen clientseitiges Rendern, Single-Page-Apps und statische Seitengenerierung, und Serverrendering können Sie später pro Route hinzufügen. Sie bringen Lösungen für Routing, Datenabruf und andere häufige Muster mit.

Von Grund auf, oder in eine Seite, die es schon gibt

Von Grund auf beginnen Sie mit einem Build-Tool: Vite, Parcel oder Rsbuild. Es gibt Ihnen einen Entwicklungsserver und einen Build-Befehl und startet mit einer reinen Client-Single-Page-App. Routing, Datenabruf und Code-Splitting wählen Sie dann selbst; react.dev schlägt Bibliotheken wie React Router, TanStack Query und SWR vor. Wählen Sie diesen Weg nur, wenn Sie diese Probleme selbst lösen wollen. Eine bestehende Seite schreiben Sie nicht neu: Geben Sie einem Element eine eindeutige id und rufen Sie createRoot darauf auf, eine Root pro UI-Teil, den React verwaltet. Eine App ganz aus React hat meist eine Root.

Quellen

Zuletzt geprüft am 5. Oktober 2026