Skip to content

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 text sich ändert → Button wird nicht neu gerendert (da label und onClick gleich 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 text sich ändert → expensiveCalculation wird 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, Main oder Footer auftritt → 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ädt About.jsx nur, 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

Frei für alle Anfänger