Appearance
Kapitel 13: Fortgeschrittene Konzepte
In diesem Kapitel lernen wir fortgeschrittene Techniken zur Performance-Optimierung und Wiederverwendbarkeit.
13.1 Komponenten wiederverwenden
🔄 Benutzerdefinierte Hooks (Custom Hooks)
Was sind Custom Hooks?
- Funktionen, die React-Hooks verwenden
- Ermöglichen Logik-Wiederverwendung
Regel: Namen müssen mit use beginnen.
Beispiel: useFetch (Daten abrufen)
jsx
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
})
.catch(err => {
setError(err.message);
setLoading(false);
});
}, [url]);
return { data, loading, error };
}
// Verwendung
function App() {
const { data, loading, error } = useFetch('https://api.example.com/users');
if (loading) return <p>Lädt...</p>;
if (error) return <p>Fehler: {error}</p>;
return <div>{/* Daten anzeigen */}</div>;
}🔧 Höhere Ordnung Komponenten (HOC) - Nur zum Verständnis
Was ist HOC?
- Eine Funktion, die eine Komponente nimmt und eine neue Komponente zurückgibt.
- Wird selten in modernen React-Anwendungen verwendet.
jsx
// HOC: withLogger
function withLogger(WrappedComponent) {
return function EnhancedComponent(props) {
useEffect(() => {
console.log(`Komponente ${WrappedComponent.name} wurde gerendert`);
});
return <WrappedComponent {...props} />;
};
}
// Verwendung
function Button({ label }) {
return <button>{label}</button>;
}
export default withLogger(Button);Hinweis: In modernen React-Anwendungen verwenden wir Custom Hooks oder Komponenten-Komposition anstelle von HOCs.
13.2 memo, useMemo, useCallback (Performance-Optimierung)
1️⃣ React.memo (Komponenten-Caching)
Problem: Eltern-Komponente wird neu gerendert → Alle Kindkomponenten werden neu gerendert.
Lösung: React.memo verhindert unnötige Renderings.
jsx
import { memo } from 'react';
const Button = memo(function Button({ label, onClick }) {
console.log('Button gerendert');
return <button onClick={onClick}>{label}</button>;
});
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
return (
<div>
<p>Zähler: {count}</p>
<Button label="Erhöhen" onClick={() => setCount(count + 1)} />
<input value={text} onChange={(e) => setText(e.target.value)} />
</div>
);
}Erklärung:
- Wenn
textsich ändert →Buttonwird nicht neu gerendert (dalabelundonClickgleich geblieben sind).
2️⃣ useMemo (Berechnungen cachen)
Problem: Teure Berechnungen werden bei jedem Rendering ausgeführt.
Lösung: useMemo speichert das Ergebnis zwischen.
jsx
import { useMemo } from 'react';
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// Teure Berechnung
const expensiveCalculation = (num) => {
console.log('Berechnung läuft...');
return num * 2;
};
const doubled = useMemo(() => {
return expensiveCalculation(count);
}, [count]); // Nur wenn count sich ändert
return (
<div>
<p>Ergebnis: {doubled}</p>
<button onClick={() => setCount(count + 1)}>Erhöhen</button>
<input value={text} onChange={(e) => setText(e.target.value)} />
</div>
);
}Erklärung:
- Wenn
textsich ändert →expensiveCalculationwird nicht erneut ausgeführt.
3️⃣ useCallback (Funktionen cachen)
Problem: Bei jedem Rendering wird eine neue Funktionsinstanz erstellt → memo funktioniert nicht.
Lösung: useCallback gibt dieselbe Funktionsinstanz zurück.
jsx
import { useState, memo, useCallback } from 'react';
const Button = memo(function Button({ onClick, label }) {
console.log('Button gerendert');
return <button onClick={onClick}>{label}</button>;
});
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// ❌ Falsch: Jedes Mal neue Funktion
// const handleClick = () => setCount(count + 1);
// ✅ Richtig: Funktion cachen
const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, []); // Leeres Array = Funktion wird nur einmal erstellt
return (
<div>
<p>Zähler: {count}</p>
<Button label="Erhöhen" onClick={handleClick} />
<input value={text} onChange={(e) => setText(e.target.value)} />
</div>
);
}13.3 Error Boundaries (Fehlerbehandlung)
🛡️ Was sind Error Boundaries?
Error Boundaries fangen JavaScript-Fehler in der UI ab und zeigen eine Fallback-UI an.
Wichtig: Error Boundaries können nur mit Klassenkomponenten erstellt werden.
📝 Error Boundary erstellen
ErrorBoundary.jsx:
jsx
import { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Fehler abgefangen:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div>
<h1>Etwas ist schiefgegangen.</h1>
<p>{this.state.error.message}</p>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;🛡️ Verwendung
jsx
import ErrorBoundary from './ErrorBoundary';
function App() {
return (
<ErrorBoundary>
<Header />
<Main />
<Footer />
</ErrorBoundary>
);
}Ergebnis:
- Wenn ein Fehler in
Header,MainoderFooterauftritt → Zeige Fallback-UI.
13.4 Lazy Loading (Code Splitting)
⚡ Was ist Lazy Loading?
Lazy Loading verzögert das Laden von Komponenten, bis sie benötigt werden.
Vorteile:
- Schnelleres initiales Laden
- Bessere Performance
📦 React.lazy und Suspense
jsx
import { lazy, Suspense } from 'react';
// Lazy Loading Komponente
const About = lazy(() => import('./About'));
const Contact = lazy(() => import('./Contact'));
function App() {
const [page, setPage] = useState('home');
return (
<div>
<nav>
<button onClick={() => setPage('home')}>Startseite</button>
<button onClick={() => setPage('about')}>Über uns</button>
<button onClick={() => setPage('contact')}>Kontakt</button>
</nav>
<Suspense fallback={<p>Lädt...</p>}>
{page === 'home' && <h1>Startseite</h1>}
{page === 'about' && <About />}
{page === 'contact' && <Contact />}
</Suspense>
</div>
);
}Erklärung:
lazy(() => import('./About')): LädtAbout.jsxnur, wenn benötigt.<Suspense fallback={...}>: Zeigt Ladeanzeige, während Komponente lädt.
13.5 Portals (Modal-Dialoge)
🚪 Was sind Portals?
Portals erlauben, Komponenten ausserhalb der normalen DOM-Hierarchie zu rendern.
Anwendungsfall: Modal-Dialoge, Tooltips, Popups.
📝 Portal erstellen
Modal.jsx:
jsx
import { createPortal } from 'react-dom';
function Modal({ children, onClose }) {
return createPortal(
<div className="modal-backdrop" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
{children}
<button onClick={onClose}>Schließen</button>
</div>
</div>,
document.getElementById('modal-root') // DOM-Element ausserhalb von #root
);
}
export default Modal;index.html:
html
<body>
<div id="root"></div>
<div id="modal-root"></div>
</body>🚪 Verwendung
jsx
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<button onClick={() => setIsModalOpen(true)}>Modal öffnen</button>
{isModalOpen && (
<Modal onClose={() => setIsModalOpen(false)}>
<h1>Modal Titel</h1>
<p>Dies ist ein Modal-Dialog.</p>
</Modal>
)}
</div>
);
}📝 Zusammenfassung
In diesem Kapitel haben wir gelernt:
- ✅ Komponenten wiederverwenden (Custom Hooks, HOC)
- ✅ Performance-Optimierung (
memo,useMemo,useCallback) - ✅ Error Boundaries (Fehlerbehandlung)
- ✅ Lazy Loading (
React.lazy,Suspense) - ✅ Portals (Modal-Dialoge)
🎯 Nächste Schritte
Im nächsten Kapitel werden wir:
- Praxis-Projekt: TodoList erstellen
- Alle bisher gelernten Konzepte anwenden
- Eine vollständige Anwendung bauen
Bereit für das erste Projekt? → Kapitel 14: Projekt - TodoList
