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.
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 />);
Ü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?
Üben · Aufgabe 4 von 7
Ordnen Sie jedem Namen von den Installationsseiten von react.dev zu, was er ist.
Üben · Aufgabe 5 von 7
Das HTML der bestehenden Seite hat <nav id="navigation"></nav>. Ergänzen Sie das Argument, sodass NavigationBar darin rendert.
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?
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>
);
}
<!-- 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
Hinweis 1
createRoot(domNode) erzeugt eine Root für das Element; angezeigt wird nichts, bis Sie render darauf aufrufen.
Hinweis 2
Halten Sie die Root in einer Variablen: const root = createRoot(domNode);
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
Hinweis 1
Ein erneuter Aufruf von render auf derselben Root ersetzt, was sie vorher gezeigt hat.
Hinweis 2
Rufen Sie createRoot ein zweites Mal auf, mit document.getElementById('newsletter').
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.
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.