12k
All articles

So beheben Sie den Fehler „Invalid Hook Call“ in React

Beheben Sie React invalid hook call Fehler, indem Sie Stacktrace, Rules of Hooks, doppelte React-Kopien und react-dom-Versionen prüfen.

OpenReplay Team
OpenReplay Team
So beheben Sie den Fehler „Invalid Hook Call“ in React

Der Fehler „Invalid Hook Call” hat drei häufige Ursachen: einen Verstoß gegen die Rules of Hooks im eigenen Code, mehr als eine React-Kopie in der App oder nicht zusammenpassende Versionen von react und react-dom. Der Stack Trace verrät Ihnen, welche davon Sie zuerst prüfen sollten.

Häufig tritt der Fehler auf, obwohl Ihr eigener Komponentencode einwandfrei ist. Sie binden per npm link eine lokale Komponentenbibliothek ein, fügen eine Abhängigkeit hinzu oder strukturieren ein Monorepo um – und der Fehler erscheint, ohne zu verraten, welche der drei Ursachen vorliegt.

Die wichtigsten Erkenntnisse

  • Befindet sich der fehlschlagende Hook-Aufruf in Ihrer eigenen Komponentendatei, liegt das Problem darin, wo der Hook aufgerufen wird; liegt er innerhalb von node_modules, ist fast immer eine zweite React-Kopie die Ursache.
  • Führen Sie npm ls react (bzw. pnpm why react oder yarn why react) aus; wenn die Ausgabe mehr als eine React-Version auflöst, sind doppelte Kopien die Ursache, und keine Änderung am Komponentencode wird das beheben.
  • Eine Komponentenbibliothek muss React in den peerDependencies deklarieren und aus dem Build-Output ausschließen; bündelt sie ihr eigenes React, erhält jede konsumierende App zwei Kopien.
  • Die Lint-Regel rules-of-hooks erkennt falsch platzierte Hook-Aufrufe, bevor der Code ausgeführt wird, aber kein Linter kann eine doppelte React-Kopie erkennen, denn dieser Fehler steckt im installierten Abhängigkeitsbaum und nicht im Quellcode.
  • In der Produktion erscheint der Fehler als minifizierter Fehler #321 – dekodieren Sie ihn daher im Error Decoder von React, bevor Sie über die Ursache spekulieren.

Was bedeutet der Fehler „Invalid Hook Call”?

React wirft diesen Fehler, sobald ein Hook außerhalb des Renderings einer Funktionskomponente ausgeführt wird, und die Meldung selbst zählt die Möglichkeiten auf:

Invalid hook call. Hooks can only be called inside of the body of a function component.
This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

Die Seite zur Invalid-Hook-Call-Warnung von React behandelt alle drei Ursachen sowie einen Auffangabschnitt für die selteneren Fälle. Der Rest dieses Artikels prüft sie in der Reihenfolge, die dem üblichen Auftreten des Fehlers entspricht.

Lesen Sie zuerst den Stack Trace

Bevor Sie irgendeine Konfiguration anfassen, beantworten Sie anhand des Stack Trace eine Frage: Liegt der Frame, der den Hook aufruft, in Ihren eigenen Quelldateien oder innerhalb von node_modules? Zeigt er auf Ihre Komponentendatei, liegt ein Verstoß gegen die Rules of Hooks vor, und die Lösung steckt in Ihrem Code. Zeigt er in eine Abhängigkeit, die bisher funktioniert hat, haben Sie mit ziemlicher Sicherheit zwei React-Kopien – und nichts, was Sie in einer Komponente ändern, wird daran etwas ändern.

UrsacheWie sie sich bestätigen lässtLösung
Verstoß gegen die Rules of HooksStack Trace zeigt in Ihre eigenen DateienHook auf die oberste Ebene einer Komponente verschieben
Zwei React-Kopiennpm ls react löst zwei Versionen aufAbhängigkeitsbaum deduplizieren (siehe unten)
Nicht passende react/react-dom-Versionennpm ls react react-dom zeigt unterschiedliche VersionenBeide gemeinsam installieren

Einen Invalid Hook Call im eigenen Code beheben

Nur zwei Regeln führen zu diesem Fehler: Hooks müssen während des Renderings einer Funktionskomponente aufgerufen werden (oder aus einem Custom Hook, den eine Komponente aufruft), und sie müssen auf der obersten Ebene dieser Komponente stehen – nicht innerhalb eines if, einer Schleife oder einer verschachtelten Funktion. Ein Hook auf Modulebene, in einem Event-Handler oder in einer einfachen Hilfsfunktion verletzt die erste Regel; ein Hook innerhalb einer Bedingung oder eines .map-Callbacks verletzt die zweite.

Der Fall mit der Hilfsfunktion überrascht viele, weil der Code durchaus vernünftig aussieht:

// Wrong: buildLink is a plain function, not a component
export function buildLink() {
  const { pathname } = useLocation(); // invalid hook call
  return `https://example.com${pathname}`;
}

// Right: call the hook in a component, pass the value down
function Page() {
  const { pathname } = useLocation();
  return <a href={buildLink(pathname)}>Canonical</a>;
}

export function buildLink(pathname) {
  return `https://example.com${pathname}`;
}

Beim Schleifenfall ist die Lösung struktureller Natur: Lagern Sie eine Kindkomponente aus, damit jedes Element seinen eigenen State besitzt.

// Wrong: one hook call per array item
function List({ items }) {
  return items.map((item) => {
    const [open, setOpen] = useState(false); // invalid hook call
    return <li key={item.id}>{item.name}</li>;
  });
}

// Right: each row is a component with its own state
function Row({ item }) {
  const [open, setOpen] = useState(false);
  return <li onClick={() => setOpen(!open)}>{item.name}</li>;
}

function List({ items }) {
  return items.map((item) => <Row key={item.id} item={item} />);
}

Warum brechen zwei React-Kopien die Hooks?

Hooks funktionieren nur, wenn Ihre App und react-dom beide dasselbe react-Modul laden. Erhält jede von beiden ihre eigene Kopie, wirft React diesen Fehler, selbst wenn jeder Hook-Aufruf in Ihrem Code genau dort steht, wo er hingehört. Prüfen Sie das als Erstes:

npm ls react     # npm
pnpm why react   # pnpm
yarn why react   # yarn

pnpm why und yarn why arbeiten von einem Paket aus rückwärts zu allem, was es hereingezogen hat – so sehen Sie genau, welche Abhängigkeit die zweite Kopie mitbringt. Zwei Situationen machen die meisten Duplikate aus:

Ein verlinktes lokales Paket. Eine Bibliothek, die per npm link oder pnpm link eingebunden wurde, löst React aus ihren eigenen node_modules auf, nicht aus Ihren. Deshalb tritt der Fehler oft genau in dem Moment auf, in dem Sie eine Komponentenbibliothek verlinken, die bei normaler Installation problemlos funktioniert hat. Die React-Dokumentation behandelt den npm link-Fall; dort besteht die Lösung darin, die Bibliothek auf das bereits in der App installierte React zu verweisen. In Vite-Projekten führen Sie die Pakete in resolve.dedupe auf, und Vite fixiert jedes davon auf eine einzige Kopie aus dem Projektstammverzeichnis:

// vite.config.js
export default {
  resolve: { dedupe: ['react', 'react-dom'] },
}

Eine Bibliothek, die React mitliefert. Deklariert ein Paket react als reguläre Abhängigkeit oder bündelt es React in seinen Build-Output, erhält jeder Konsument zwei Kopien. Die Lösung auf Bibliotheksseite besteht darin, React in den peerDependencies mit dem unterstützten Versionsbereich zu deklarieren und es im Build als extern zu markieren. Der Workaround auf App-Seite erzwingt eine einzige Auflösung, wobei der Feldname vom Paketmanager abhängt. npm liest overrides, wobei $react bedeutet: „dieselbe Version, die ich selbst für react deklariere”:

{ "overrides": { "react": "$react", "react-dom": "$react-dom" } }

yarn liest stattdessen resolutions, mit einer einfachen Version als Wert. Setzen Sie nicht beide Felder in eine Datei; jeder Manager ignoriert den Schlüssel des anderen.

Nicht zusammenpassende Versionen von react und react-dom

react und react-dom werden als Paar ausgeliefert – prüfen Sie also beide und installieren Sie sie mit einem einzigen Befehl. Führen Sie npm ls react react-dom aus; unterscheiden sich die beiden Versionen, installieren Sie sie gemeinsam neu (npm install react react-dom), damit sie auf dasselbe Release aufgelöst werden. Das ist die am schnellsten auszuschließende Ursache, und wer sie früh ausschließt, jagt keinen Phantom-Bugs im Code hinterher.

Wie erkennen Sie den Fehler früher mit einem Linter?

Das Paket eslint-plugin-react-hooks markiert jede code-bedingte Ursache dieses Fehlers bereits während des Schreibens. Mit der Flat Config von ESLint:

// eslint.config.js
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';

export default defineConfig([reactHooks.configs.flat.recommended]);

Bei ESLint-Versionen unter 9.0.0 lautet die Legacy-Form "extends": ["plugin:react-hooks/recommended"]. Next.js-Projekte erhalten diese Regeln bereits über eslint-config-next. Die Regel rules-of-hooks erkennt bedingte und falsch platzierte Hook-Aufrufe, bevor der Code überhaupt ausgeführt wird. Kein Linter kann jedoch eine doppelte React-Kopie oder eine Versionsabweichung erkennen; diese Fehler existieren ausschließlich im installierten Abhängigkeitsbaum und treten daher erst zur Laufzeit zutage.

Die Produktionsvariante: Minifizierter Fehler #321

In einem Produktions-Build erscheint dieser Fehler als minifizierter Fehlercode statt als vollständige Meldung – dekodieren Sie den Code also, bevor Sie annehmen, welches Problem vorliegt. React-Fehler #321 entspricht dem Invalid-Hook-Call-Text; das zuerst zu bestätigen bewahrt Sie davor, die falsche Invariante zu debuggen. Der minifizierte Stack nennt selten die Komponente, die den Fehler geworfen hat, was den Fall der doppelten Kopie in der Produktion besonders schwer nachvollziehbar macht. Ein Session-Replay-Tool wie OpenReplay, das den Konsolenfehler zusammen mit der Route und der vorangegangenen Interaktion erfasst, zeigt, welcher Komponentenbaum im Moment des Fehlers gemountet wurde – und das weist in der Regel auf den Lazy-Loaded-Chunk oder das Drittanbieter-Widget hin, das die zweite React-Kopie hereingetragen hat.

Beginnen Sie beim Stack Trace

Behandeln Sie den Fehler als Wegweiser-Problem und nicht als Rätsel: Der Stack Trace schickt Sie entweder in Ihre eigene Komponente (Platzierung des Hooks korrigieren) oder in den Abhängigkeitsbaum (npm ls react ausführen und deduplizieren). Beginnen Sie mit diesem einen Befehl; er klärt die verwirrendste der drei Ursachen in Sekunden, und alles danach ist eine bekannte Lösung.

FAQs

Muss ein Custom Hook mit „use“ beginnen, um den Invalid-Hook-Call-Fehler zu vermeiden?

Nein. Das Präfix „use“ verursacht oder verhindert diesen Laufzeitfehler nie, denn React prüft Hook-Namen zur Laufzeit nicht. Das Präfix ist für das Tooling relevant: eslint-plugin-react-hooks stützt sich darauf, um Hooks zu erkennen und die Rules of Hooks durchzusetzen – ein falsch benannter Custom Hook entgeht den Lint-Prüfungen also stillschweigend. Benennen Sie ihn mit dem Präfix um, damit Verstöße bereits beim Schreiben statt erst im Browser gemeldet werden.

Kann ich Hooks innerhalb einer Klassenkomponente aufrufen?

Nein. Hooks funktionieren nur in Funktionskomponenten und in Custom Hooks, die von ihnen aufgerufen werden. Der Aufruf von useState oder useContext innerhalb einer Klassenmethode wirft daher den Invalid-Hook-Call-Fehler. Um einen Hook zusammen mit einer Klasse zu nutzen, die Sie nicht umschreiben können, erstellen Sie eine kleine Funktionskomponente, die den Hook aufruft und das Ergebnis als Props an die Klassenkomponente weitergibt – oder wandeln Sie die Klasse in eine Funktionskomponente um.

Können zwei React-Kopien jemals fehlerfrei auf derselben Seite laufen?

Ja. Zwei Apps auf einer Seite können problemlos jeweils ihr eigenes React laden, etwa wenn verschiedene Teams sie getrennt ausliefern. Der Fehler tritt nur auf, wenn eine Komponente und die sie rendernde react-dom-Instanz sich uneinig darüber sind, welches react-Modul sie verwenden. Getrennte Kopien für sich genommen sind unproblematisch; sie brechen erst, sobald sie sich einen gemeinsamen Render-Baum teilen.

Behebt das Löschen von node_modules und eine Neuinstallation doppelte React-Kopien?

Nur dann, wenn das Duplikat aus einem veralteten oder widersprüchlichen Installationszustand stammte, denn eine frische Installation erlaubt es dem Paketmanager, den Abhängigkeitsbaum zu deduplizieren. Deklariert eine Abhängigkeit react als reguläre Abhängigkeit, bündelt sie React in ihren Build-Output, oder haben Sie sie lokal per npm link eingebunden, kehrt die zweite Kopie bei jeder Installation zurück. Diese Fälle erfordern einen overrides- oder resolutions-Eintrag, eine peerDependencies-Korrektur in der Bibliothek oder eine Deduplizierung auf Bundler-Ebene.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.