Zum Inhalt springen
aviral gupta

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

React Developer Tools

Nach dieser Lektion installieren Sie die React Developer Tools, finden eine Komponente im Components-Tab, lesen und ändern ihre Props und ihren State und benennen Komponenten so, dass Sie sie finden.

Lektion 4 von 4 in B4 Ein Projekt einrichten

Ende des Moduls

Danach können Sie

  • Die React Developer Tools als Browser-Erweiterung installieren, oder für Safari als Paket react-devtools
  • Mit dem Components-Tab eine Komponente finden und ihre Props und ihren State lesen oder ändern
  • Den Komponentenbaum einer Seite vorhersagen und Komponenten auffindbar benennen
  1. Aufwärmen · Aufgabe 1 von 7

    Zum Aufwärmen aus Lektion B4.3: Welche Regel setzt .oxlintrc.json der React-Vorlage von Vite auf error?

  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie voraus, bevor Sie weiterlesen. Sie öffnen den Components-Tab für diese Seite. Welchen Baum zeigt er? Er ist hier als Eltern(Kinder) geschrieben, mit dem key nach #.

    function Header() {
      return <h1>To do</h1>;
    }
    
    function TodoItem({ text }) {
      return <li>{text}</li>;
    }
    
    function TodoList() {
      return (
        <ul>
          <TodoItem key="1" text="Tea" />
          <TodoItem key="2" text="Milk" />
        </ul>
      );
    }
    
    function App() {
      return (
        <main>
          <Header />
          <TodoList />
        </main>
      );
    }
  3. Üben · Aufgabe 3 von 7

    Ordnen Sie jedem Ort, an dem Ihre React-App läuft, den Weg zu, den react.dev zum Untersuchen nennt.

  4. Üben · Aufgabe 4 von 7

    Ergänzen Sie die Adresse des Script-Tags, das Ihre Seite mit dem eigenständigen react-devtools verbindet, wie react.dev es zeigt.

    <script src=""></script>
  5. Üben · Aufgabe 5 von 7

    Beide Komponenten sind Pfeilfunktionen. Welche Namen gibt JavaScript ihnen, als [Cart.name, parts[0].name]?

    const Cart = () => <p>Cart</p>;
    const parts = [() => <p>Part</p>];
  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Counter kopiert seine Prop initial in den State. Er rendert mit initial 0; dann rendert ihn sein Elternteil erneut mit initial 5, also lauten seine Props in den DevTools jetzt initial: 5. Was zeigt der Button?

    function Counter({ initial }) {
      const [count, setCount] = useState(initial);
      return <button onClick={() => setCount(count + 1)}>Coffee: {count}</button>;
    }
  7. Anwenden · Aufgabe 7 von 7

    Mini-Aufgabe: Untersuchen Sie Ihren Warenkorb. Installieren Sie die React Developer Tools für Ihren Browser (oder react-devtools für Safari). Setzen Sie den Warenkorb aus Lektion B3.6 als src/App.jsx in Ihr Vite-Projekt, mit data.js daneben, und starten Sie npm run dev. Finden Sie im Components-Tab ShoppingCart, ProductList und Cart. Fügen Sie zweimal Bread hinzu und lesen Sie die Prop items von Cart und den State von ShoppingCart. Nutzen Sie den Inspect-Button auf einer Warenkorbzeile. Ändern Sie dann eine Menge im State von ShoppingCart und beobachten Sie die Seite.

    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

Die Namen, die der Baum zeigen wird

Shop rendert einen Header aus dessen eigener Datei, zwei ProductRow-Komponenten und ein Badge, dessen Datei eine Pfeilfunktion ohne Namen exportiert. Die letzte Zeile gibt den Namen aus, den JavaScript jeder Komponentenfunktion gibt: Nach diesen Namen suchen Sie im Components-Tab. Sie läuft, wenn main.jsx lädt, also erscheint ihre Ausgabe vor dem Schnappschuss von #root.

main.jsx

import Header from './Header.jsx';
import Badge from './Badge.jsx';

function ProductRow({ name }) {
  return <li>{name}</li>;
}

export default function Shop() {
  return (
    <>
      <Header title="Tea shop" />
      <ul>
        <ProductRow name="Green tea" />
        <ProductRow name="Black tea" />
      </ul>
      <Badge count={2} />
    </>
  );
}

// The name JavaScript gives each component function.
console.log(Shop.name, Header.name, ProductRow.name, JSON.stringify(Badge.name));

Header.jsx

export default function Header({ title }) {
  return <h1>{title}</h1>;
}

Badge.jsx

// No name: react.dev discourages this.
export default ({ count }) => <p>{count} items in the cart</p>;

Ausführen mit

npm run dev

Ausgabe

Shop Header ProductRow "default"
<!-- #root -->
<div id="root"><h1>Tea shop</h1><ul><li>Green tea</li><li>Black tea</li></ul><p>2 items in the cart</p></div>
  • Badge.jsx exportiert export default ({ count }) => …: Die Funktion hat keinen eigenen Namen, also nennt JavaScript sie default.
  • Der Baum dieser Seite ist Shop, darin Header, zweimal ProductRow und die namenlose Komponente; die Elemente ul und li stehen nicht im Baum.
  • Reparieren Sie Badge, indem Sie ihm einen Namen geben: export default function Badge({ count }) { … }. Der Importname in main.jsx benennt die Funktion nicht um.
  • react.dev: Von Komponenten ohne Namen wird abgeraten, weil sie das Debuggen erschweren.
Ä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

Komponenten, die Sie im Baum finden

Shop macht alles inline, also zeigt der Components-Tab nur Shop. Schreiben Sie zwei Komponenten und exportieren Sie sie mit Namen: ProductRow({ product, onAdd }) rendert ein <li> mit dem Produktnamen, einem Leerzeichen und einem Button "Add " plus Name, der onAdd(product) aufruft; CartSummary({ count }) rendert <p>Cart: {count} items</p>. Rendern Sie dann in Shop eine ProductRow pro Produkt, mit key, und CartSummary.

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

    Schreiben Sie jede als export function Name(props) { … } über Shop, damit sie einen Namen hat und die Tests sie importieren können.

  2. Hinweis 2

    ProductRow gibt das <li> aus dem map zurück, mit {product.name}, {' '} und dem Button darin.

  3. Hinweis 3

    In Shop: products.map(product => <ProductRow key={product.id} product={product} onAdd={() => setCount(count + 1)} />), dann <CartSummary count={count} />.

Eine Lösung zeigen

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

import { useState } from 'react';

const products = [
  { id: 'tea', name: 'Tea' },
  { id: 'coffee', name: 'Coffee' }
];

export function ProductRow({ product, onAdd }) {
  return (
    <li>
      {product.name}{' '}
      <button onClick={() => onAdd(product)}>Add {product.name}</button>
    </li>
  );
}

export function CartSummary({ count }) {
  return <p>Cart: {count} items</p>;
}

export default function Shop() {
  const [count, setCount] = useState(0);

  return (
    <>
      <h1>Shop</h1>
      <ul>
        {products.map(product => (
          <ProductRow key={product.id} product={product} onAdd={() => setCount(count + 1)} />
        ))}
      </ul>
      <CartSummary count={count} />
    </>
  );
}
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';

const products = [
  { id: 'tea', name: 'Tea' },
  { id: 'coffee', name: 'Coffee' }
];

// TODO: write ProductRow({ product, onAdd }) and CartSummary({ count }),
// export them, and render them below instead of the inline markup.

export default function Shop() {
  const [count, setCount] = useState(0);

  return (
    <>
      <h1>Shop</h1>
      <ul>
        {products.map(product => (
          <li key={product.id}>
            {product.name}{' '}
            <button onClick={() => setCount(count + 1)}>Add {product.name}</button>
          </li>
        ))}
      </ul>
      <p>Cart: {count} items</p>
    </>
  );
}

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 Shop, * as parts from './main.jsx';

test('ProductRow zeigt sein Produkt und gibt es an onAdd weiter', async () => {
  const user = userEvent.setup();
  const {ProductRow} = parts;
  assert.equal(typeof ProductRow, 'function', 'main.jsx exportiert keine Funktion ProductRow');
  const added = [];
  render(<ul><ProductRow product={{ id: 'tea', name: 'Tea' }} onAdd={(product) => added.push(product)} /></ul>);
  assert.equal(screen.getByRole('listitem').textContent, 'Tea Add Tea', 'die Zeile lautet ' + JSON.stringify(screen.getByRole('listitem').textContent));
  await user.click(screen.getByRole('button', {name: 'Add Tea'}));
  assert.equal(added[0]?.name, 'Tea', 'onAdd erhielt ' + JSON.stringify(added));
});

test('CartSummary zeigt die Anzahl, die es erhält', () => {
  const {CartSummary} = parts;
  assert.equal(typeof CartSummary, 'function', 'main.jsx exportiert keine Funktion CartSummary');
  render(<CartSummary count={2} />);
  assert.equal(document.body.textContent, 'Cart: 2 items', 'CartSummary mit count 2 lautet ' + JSON.stringify(document.body.textContent));
});

test('Der Shop zählt, was Sie hinzufügen', async () => {
  const user = userEvent.setup();
  render(<Shop />);
  await user.click(screen.getByRole('button', {name: 'Add Tea'}));
  await user.click(screen.getByRole('button', {name: 'Add Coffee'}));
  assert.equal(screen.queryByText(/^Cart:/)?.textContent, 'Cart: 2 items', 'nach zwei Klicks zeigt die Seite ' + JSON.stringify(document.body.textContent));
});

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

Der falschen Prop zum Elternteil folgen

Im Warenkorb liegen 2 Tea und 1 Coffee, aber die Zusammenfassung sagt Cart: 2 items. Im Components-Tab zeigt CartSummary die Props count: 2 und wird von Cart gerendert. CartSummary zeigt an, was es bekommt, also steckt der Fehler im Elternteil, das count übergibt. Reparieren Sie ihn in Cart: Zählen Sie die Mengen, damit die Zusammenfassung 3 zeigt, und 4 nach Add Tea.

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

    items.length zählt die Zeilen, nicht die Tassen: 2 Zeilen, 3 Tassen.

  2. Hinweis 2

    Addieren Sie item.quantity über items, beim Rendern: items.reduce((sum, item) => sum + item.quantity, 0).

  3. Hinweis 3

    Speichern Sie das Ergebnis in const count und übergeben Sie count={count} an CartSummary. Legen Sie es nicht in den State.

Eine Lösung zeigen

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

import { useState } from 'react';

function CartLine({ item }) {
  return <li>{item.name} × {item.quantity}</li>;
}

function CartSummary({ count }) {
  return <p>Cart: {count} items</p>;
}

export default function Cart() {
  const [items, setItems] = useState([
    { id: 'tea', name: 'Tea', quantity: 2 },
    { id: 'coffee', name: 'Coffee', quantity: 1 }
  ]);

  function addTea() {
    setItems(items.map(item =>
      item.id === 'tea' ? { ...item, quantity: item.quantity + 1 } : item
    ));
  }

  const count = items.reduce((sum, item) => sum + item.quantity, 0);

  return (
    <>
      <ul>
        {items.map(item => <CartLine key={item.id} item={item} />)}
      </ul>
      <CartSummary count={count} />
      <button onClick={addTea}>Add Tea</button>
    </>
  );
}
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';

function CartLine({ item }) {
  return <li>{item.name} × {item.quantity}</li>;
}

function CartSummary({ count }) {
  return <p>Cart: {count} items</p>;
}

export default function Cart() {
  const [items, setItems] = useState([
    { id: 'tea', name: 'Tea', quantity: 2 },
    { id: 'coffee', name: 'Coffee', quantity: 1 }
  ]);

  function addTea() {
    setItems(items.map(item =>
      item.id === 'tea' ? { ...item, quantity: item.quantity + 1 } : item
    ));
  }

  return (
    <>
      <ul>
        {items.map(item => <CartLine key={item.id} item={item} />)}
      </ul>
      {/* DevTools shows CartSummary with props count: 2. */}
      <CartSummary count={items.length} />
      <button onClick={addTea}>Add Tea</button>
    </>
  );
}

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 Cart from './main.jsx';

test('Die Zusammenfassung zählt jedes Stück: 2 Tea und 1 Coffee sind 3', () => {
  render(<Cart />);
  assert.equal(screen.queryByText(/^Cart:/)?.textContent, 'Cart: 3 items', 'die Zusammenfassung lautet ' + JSON.stringify(screen.queryByText(/^Cart:/)?.textContent));
});

test('Add Tea erhöht die Zeile und die Zusammenfassung um eins', async () => {
  const user = userEvent.setup();
  render(<Cart />);
  await user.click(screen.getByRole('button', {name: 'Add Tea'}));
  assert.equal(screen.queryByText(/^Tea/)?.textContent, 'Tea × 3', 'die Zeile Tea lautet ' + JSON.stringify(screen.queryByText(/^Tea/)?.textContent));
  assert.equal(screen.queryByText(/^Cart:/)?.textContent, 'Cart: 4 items', 'die Zusammenfassung lautet ' + JSON.stringify(screen.queryByText(/^Cart:/)?.textContent));
});

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

Den State des Elternteils beim Rendern setzen

import { useState } from 'react';

function CartBadge({ items, onCount }) {
  onCount(items.length); // tells the parent while rendering
  return <span>{items.length} items</span>;
}

export default function Shop() {
  const [count, setCount] = useState(0);
  const items = ['Tea', 'Coffee'];
  return (
    <>
      <h1>Shop ({count})</h1>
      <CartBadge items={items} onCount={setCount} />
    </>
  );
}

Was React ausgibt

Cannot update a component (`Shop`) while rendering a different component (`CartBadge`).

Warum, und die Lösung

CartBadge setzt den State von Shop, während es rendert, und das erlaubt React nicht. Die Warnung nennt beide Komponenten, wie der Components-Tab, also wissen Sie, wo Sie suchen müssen. Shop hat die items schon: Berechnen Sie const count = items.length; in Shop und lassen Sie onCount weg. Bei einer namenlosen Komponente stünde in der Warnung stattdessen Unknown.

Ein ganzes Modul als Komponente importieren

import * as CartSummary from './CartSummary.jsx';

export default function Shop() {
  return <CartSummary count={2} />;
}

Was React ausgibt

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

Warum, und die Lösung

import * as CartSummary liefert das Modulobjekt, mit der Komponente in seiner Eigenschaft default, also soll React mit <CartSummary /> ein Objekt rendern. Importieren Sie den Default-Export selbst: import CartSummary from './CartSummary.jsx';.

Eine namenlose Komponente in einer Warnung

import ProductList from './ProductList.jsx';

export default function Shop() {
  return <ProductList names={['Tea', 'Coffee']} />;
}

Was React ausgibt

Check the render method of `default`.

Warum, und die Lösung

Die Liste hat keine keys, also warnt React und nennt die Komponente, deren Rendern die Liste zurückgab: default, den Namen, den JavaScript dem anonymen Pfeil gab. Bei mehreren solchen Dateien sagt Ihnen das nicht, welche gemeint ist. Benennen Sie die Funktion, export default function ProductList({ names }) { … }, und geben Sie jedem <li> einen key: <li key={name}>.

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

Installieren

Mit den React Developer Tools untersuchen Sie React-Komponenten, ändern Props und State und finden Performance-Probleme. Am einfachsten geht es mit der Browser-Erweiterung, die react.dev für Chrome, Firefox und Edge aufführt. Auf einer Website mit React sehen Sie dann in den Entwicklerwerkzeugen des Browsers zwei neue Panels: Components und Profiler. Für andere Browser, etwa Safari, installieren Sie das npm-Paket global, npm install -g react-devtools, starten es mit react-devtools in einem Terminal, setzen <script src="http://localhost:8097"></script> an den Anfang des <head> Ihrer Seite und laden neu. React-Native-Apps nutzen React Native DevTools.

Der Components-Tab

Der Components-Tab zeigt Ihre Komponenten als Baum: Auf dem Screenshot von react.dev enthält App eine List, die drei ListItem-Komponenten mit key="1" bis key="3" enthält. Die Elemente <ul> und <li>, die sie zurückgeben, stehen nicht darin; die zeigt der Elements-Tab. Wählen Sie eine Komponente aus, um ihre Props, ihre Hooks (dort liegt der State) und die Komponenten zu sehen, die sie gerendert haben. Um die Komponente hinter etwas auf der Seite zu finden, klicken Sie auf den Inspect-Button oben links im Tab und dann auf das Element. Dort können Sie Props und State ändern und zusehen, wie sich die Seite aktualisiert. Mit Strict Mode erscheinen Logs des zweiten Renderns abgeblendet.

Namen machen Komponenten auffindbar

Der Baum zeigt jede Komponente mit ihrem Namen, und der Name einer JavaScript-Funktion ist der, unter dem sie im Code steht: function Cart() heißt Cart, und const Cart = () => … heißt ebenfalls Cart. Ein Pfeil ohne Namen, den er übernehmen könnte, etwa export default () => …, heißt default, und Reacts eigene Warnungen nennen eine namenlose Komponente Unknown. react.dev rät von Komponenten ohne Namen ab, weil sie das Debuggen erschweren: Geben Sie Ihren Komponentenfunktionen und ihren Dateien immer sprechende Namen. Eine Komponente, die Sie als Funktion aufrufen, Row(), statt sie als <Row /> zu rendern, steht gar nicht im Baum.

Quellen

Zuletzt geprüft am 5. Oktober 2026