Custom Hooks und Rules of Hooks in React
Custom Hooks und Rules of Hooks
~12 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Zum Abschluss von Phase 2: die zwei festen Regeln, die für ALLE Hooks gelten, und wie Sie eigene, wiederverwendbare Hooks bauen, indem Sie bestehende Hooks kombinieren.
Die zwei Rules of Hooks
- Hooks nur auf oberster Ebene aufrufen. Niemals innerhalb von
if-Blöcken, Schleifen oder verschachtelten Funktionen – React verlässt sich auf die exakt gleiche REIHENFOLGE der Hook-Aufrufe bei jedem Rendern, um zu wissen, welcher State zu welchemuseState-Aufruf gehört. - Hooks nur aus React-Funktionskomponenten oder anderen Custom Hooks aufrufen. Niemals aus normalen JavaScript-Funktionen oder Klassen-Komponenten.
// FALSCH – Hook in einer Bedingung:
function Beispiel({ zeigeZaehler }) {
if (zeigeZaehler) {
const [anzahl, setAnzahl] = useState(0); // ✗ verboten
}
// ...
}
// RICHTIG – Hook immer aufrufen, Bedingung erst im JSX:
function Beispiel({ zeigeZaehler }) {
const [anzahl, setAnzahl] = useState(0); // ✓ immer auf oberster Ebene
return zeigeZaehler ? <p>{anzahl}</p> : null;
}Achtung: Verstößt Ihr Code gegen diese Regeln, meldet die "react-hooks/rules-of-hooks"-ESLint-Regel (standardmäßig in jedem Vite-React-Projekt aktiv) das SOFORT beim Speichern im Editor – Sie müssen sich diese Regeln also nicht auswendig merken, nur verstehen, WARUM sie existieren.
Was ist ein Custom Hook?
Ein "Custom Hook" ist einfach eine ganz normale JavaScript-Funktion, die (a) mit use beginnt (reine Namenskonvention, aber wichtig – so erkennt React UND die ESLint-Regel, dass diese Funktion selbst wiederum andere Hooks aufrufen darf) und (b) einen oder mehrere eingebaute Hooks im Inneren verwendet. Wiederkehrende Hook-Kombinationen lassen sich so sauber wiederverwenden, statt sie in jeder Komponente zu duplizieren.
hooks/useDocumentTitle.js: unseren ersten Custom Hook bauen
Erinnern Sie sich an den document.title-Effekt aus Kapitel 8? Den lagern wir jetzt in einen wiederverwendbaren Hook aus. Legen Sie src/hooks/useDocumentTitle.js an:
import { useEffect } from 'react';
export function useDocumentTitle(title) {
useEffect(() => {
document.title = title;
}, [title]);
}Beachten Sie: [title] statt eines leeren Arrays – so aktualisiert sich der Tab-Titel automatisch neu, sobald sich title ändert, nicht nur einmal beim ersten Rendern. Das ist ein Verbesserung gegenüber dem Original aus Kapitel 8.
AuthContext.jsx erweitern: ein useAuth-Hook
Auch useContext(AuthContext) aus Kapitel 11 lässt sich in einen eigenen Hook verpacken – üblich in echten React-Projekten, weil es (a) den Import von AuthContext selbst aus den Komponenten verbirgt und (b) einen hilfreichen Fehler wirft, falls jemand useAuth() versehentlich AUSSERHALB von AuthProvider aufruft. Erweitern Sie src/context/AuthContext.jsx:
import { createContext, useContext, useState } from 'react';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
function login(username) {
setUser({ username });
}
function logout() {
setUser(null);
}
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (context === null) {
throw new Error('useAuth() muss innerhalb von <AuthProvider> verwendet werden.');
}
return context;
}AuthContext selbst wird jetzt NICHT mehr exportiert (kein export mehr vor const AuthContext) – von außen wird nur noch AuthProvider und useAuth verwendet, AuthContext ist reines Implementierungsdetail dieser Datei geworden.
App.jsx aktualisieren: beide Custom Hooks nutzen
import { useMemo, useRef, useState } from 'react';
import ProductCard from './components/ProductCard';
import { useAuth } from './context/AuthContext';
import { useDocumentTitle } from './hooks/useDocumentTitle';
const SAMPLE_PRODUCTS = [
{ sku: 'schuhe-01', name: 'Wanderschuhe', price: 89.99, imageUrl: 'https://picsum.photos/seed/1/300' },
{ sku: 'rucksack-01', name: 'Trekking-Rucksack', price: 59.5, imageUrl: 'https://picsum.photos/seed/2/300' },
{ sku: 'jacke-01', name: 'Regenjacke', price: 74.0, imageUrl: 'https://picsum.photos/seed/3/300' },
];
function App() {
const { user, login, logout } = useAuth();
const [query, setQuery] = useState('');
const searchInputRef = useRef(null);
useDocumentTitle(`Produktkatalog (${SAMPLE_PRODUCTS.length} Produkte)`);
const gefiltert = SAMPLE_PRODUCTS.filter((produkt) =>
produkt.name.toLowerCase().includes(query.toLowerCase())
);
const teuerstesProdukt = useMemo(() => {
return gefiltert.reduce(
(max, p) => (p.price > (max?.price ?? 0) ? p : max),
null
);
}, [gefiltert]);
return (
<div className="app">
<header className="app-header">
<h1>Produktkatalog</h1>
{user ? (
<p>Angemeldet als {user.username} <button onClick={logout}>Abmelden</button></p>
) : (
<button onClick={() => login('Max Mustermann')}>Anmelden</button>
)}
</header>
<input
ref={searchInputRef}
type="text"
placeholder="Produkt suchen..."
value={query}
onChange={(event) => setQuery(event.target.value)}
/>
{teuerstesProdukt && (
<p>Teuerstes Produkt: {teuerstesProdukt.name} ({teuerstesProdukt.price.toFixed(2)} €)</p>
)}
<div className="product-grid">
{gefiltert.map((produkt) => (
<ProductCard
key={produkt.sku}
name={produkt.name}
price={produkt.price}
imageUrl={produkt.imageUrl}
onSelect={() => alert(produkt.name + ' angeklickt')}
/>
))}
</div>
</div>
);
}
export default App;Beachten Sie den fehlenden useEffect/useContext-Import in App.jsx jetzt – beide stecken versteckt in unseren eigenen Hooks. App.jsx selbst liest sich dadurch deutlich klarer: "Ich nutze Auth, ich setze einen Dokumenttitel", ohne die Details zu kennen. Damit ist Phase 2 (Hooks) abgeschlossen.
Projektstruktur nach diesem Kapitel
produktkatalog-web/
├── index.html
├── package.json
├── vite.config.js
└── src/
├── main.jsx
├── App.jsx (nutzt jetzt useAuth + useDocumentTitle)
├── index.css
├── context/
│ └── AuthContext.jsx (erweitert: useAuth-Hook)
├── hooks/
│ └── useDocumentTitle.js ← NEU
└── components/
└── ProductCard.jsxTipp: Eine gute Faustregel, WANN sich ein Custom Hook lohnt: Sobald Sie denselben Hook-Code (oder dieselbe Hook-Kombination) in einer zweiten Komponente kopieren würden, ist das ein starkes Signal, ihn stattdessen in einen Custom Hook auszulagern.