Der Spickzettel zum Kern des Seminars React v19 Grundlagen & Moderne Features. Für die Bibliotheken drumherum — TanStack, Zustand, Zod, shadcn/ui — gibt es ein eigenes Blatt: React v19 Ökosystem.
Komponenten
// Props rein, JSX raus — Komponentennamen groß schreiben
function UserCard({ name, role }) {
return <article className="card"><h3>{name}</h3><p>{role}</p></article>;
}
// Zusammensetzen über children statt über Flags konfigurieren
function Panel({ title, children }) {
return <section><h2>{title}</h2>{children}</section>;
}
Modul: Komponenten-Architektur
Hooks
// Updater-Form, wenn der neue Wert vom alten abhängt
const [count, setCount] = useState(0);
setCount((c) => c + 1);
// Effekt mit Cleanup — der Rückgabewert räumt auf
useEffect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);
// Reducer für verzahnte Übergänge — ohne Komponente testbar
const [state, dispatch] = useReducer(reducer, { count: 0 });
// Memoisieren erst, wenn der Engpass belegt ist
const teuer = useMemo(() => berechne(items), [items]);
const onPick = useCallback((id) => select(id), []);
Ein Custom Hook beginnt mit use, kapselt Logik und gibt kein JSX zurück.
Modul: State Management & Hooks
Setup mit Vite
npm create vite@latest my-app -- --template react
cd my-app && npm install && npm run dev
// vite.config.js
export default { server: { port: 3000, open: true } };
Umgebungsvariablen brauchen das VITE_-Präfix und werden über
import.meta.env gelesen — nicht über process.env:
# .env.local (gehört in .gitignore)
VITE_API_URL=https://api.example.com
Modul: Build Tools & Setup
Routing
const router = createBrowserRouter([
{ path: "/", element: <Layout />, children: [
{ index: true, element: <Home /> },
{ path: "about", element: <About /> },
]},
]);
// Lazy laden — lazy() erwartet einen Default-Export
const Dashboard = lazy(() => import("./Dashboard"));
<Suspense fallback={<Spinner />}><Dashboard /></Suspense>
// Guard
function RequireAuth({ children }) {
const { user } = useAuth();
const loc = useLocation();
if (!user) return <Navigate to="/login" state={{ from: loc }} />;
return children;
}
React 19 nimmt <title> und <meta> direkt in der Komponente entgegen:
<title>{p.name} — Shop</title>
<meta name="description" content={p.teaser} />
Modul: Routing & Navigation
Formulare
const { register, handleSubmit, reset, formState: { errors } } = useForm({
defaultValues: { name: "" },
});
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("email", { required: true })} />
{errors.email && <span>Pflichtfeld</span>}
</form>
// Nachträglich befüllen: defaultValues ändern sich nicht von selbst
useEffect(() => { if (user) reset(user); }, [user, reset]);
Modul: Form Handling
Testing mit Vitest
import { describe, it, expect } from "vitest";
describe("sum", () => {
it("addiert zwei Zahlen", () => expect(1 + 2).toBe(3));
});
Für Komponententests braucht die Config environment: "jsdom". user-event
bildet echte Interaktion besser ab als fireEvent — und will awaited
werden. Für Elemente, die fehlen dürfen, queryBy* statt getBy* nehmen:
getBy* wirft.
Modul: Testing mit Vitest
Typische Fallen
{count && <Badge />}zeigt bei0eine sichtbare Null.onClick={handler()}feuert beim Rendern statt beim Klick.classstattclassName,forstatthtmlFor.keyist keine normale Prop — sie erreicht das Kind nicht.- Fehlende Dependencies → veraltete Werte;
exhaustive-depsnicht per Ignore-Kommentar stillstellen. - Ableitungen im Effekt statt beim Rendern erzeugen unnötige Renders.
<Outlet />vergessen → Kind-Routen erscheinen nicht.<a href>statt<Link>→ voller Seiten-Reload.<Suspense>ohne Fallback → leerer Bildschirm beim Laden.registervergessen → das Feld liefert keinen Wert.value-Prop am Input gesetzt → das Feld wird controlled, unbeabsichtigt..env.localnicht in.gitignore→ Secrets landen im Repo.