Kodokon kodokon.com

Eigene Hooks: Logik extrahieren und wiederverwenden

Extrahiere zustandsbehaftete Logik in wiederverwendbare, komponierbare und testbare Hooks.

8 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Ein eigener Hook ist eine Funktion, deren Name mit use beginnt und die andere Hooks aufruft. Er extrahiert zustandsbehaftete Logik, nicht geteilten Zustand: Jede Komponente, die ihn aufruft, erhält ihr eigenes useState und useEffect, vollständig isoliert. Er ist Reacts direktestes Werkzeug, um Logik auszulagern - wo HOCs und Render-Props dem Baum Schichten hinzufügten, schlüpft ein Hook in den Körper der Komponente, lässt sich leicht komponieren und ist einfach zu testen.

JAVASCRIPT
import { useEffect, useState } from "react";

function useDebouncedValue(value, delay = 300) {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const id = setTimeout(() => {
      setDebounced(value);
    }, delay);
    return () => clearTimeout(id);
  }, [value, delay]);

  return debounced;
}
Die Aufräumfunktion bricht bei jeder Änderung den vorherigen Timer ab.

Das use-Präfix ist nicht dekorativ: Es ist das, was dem ESLint-Plugin react-hooks erlaubt, die Regeln der Hooks innerhalb deiner Funktion zu prüfen - bedingungslose Aufrufe, auf oberster Ebene. Achte auch auf die Form der zurückgegebenen API: ein [value, setValue]-Tupel, wenn die Nutzung useState spiegelt, ein Objekt mit benannten Schlüsseln, sobald es mehr als zwei Dinge offenzulegen gibt.

JAVASCRIPT
function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const stored = localStorage.getItem(key);
    return stored !== null
      ? JSON.parse(stored)
      : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}
Verzögerte Initialisierung: localStorage wird nur beim Mounten gelesen.

Hooks komponieren: Ein Hook kann andere Hooks aufrufen, auch deine eigenen. So entstehen lesbare fachliche Abstraktionen, aufgebaut auf generischen Bausteinen. Beachte die Kombination von AbortController mit der Aufräumfunktion: Ohne sie kann eine Anfrage, die für eine ältere Eingabe abgeschickt wurde, nach der neuesten antworten und deren Ergebnis überschreiben - die klassische Race Condition von Suchfeldern.

JAVASCRIPT
function useUserSearch(query) {
  const debounced = useDebouncedValue(query, 400);
  const [results, setResults] = useState([]);

  useEffect(() => {
    if (debounced === "") {
      setResults([]);
      return;
    }
    const controller = new AbortController();
    fetch("/api/search?q=" + debounced, {
      signal: controller.signal,
    })
      .then((res) => res.json())
      .then(setResults)
      .catch(() => {});
    return () => controller.abort();
  }, [debounced]);
  return results;
}
Der fachliche Hook baut auf dem generischen Hook auf.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Wofür ist das use-Präfix eines eigenen Hooks eigentlich da?
    • Es ist eine rein kosmetische Konvention, von Klassen geerbt
    • Es erlaubt dem Linter, die Funktion als Hook zu erkennen und die Regeln der Hooks darin zu prüfen
    • React weigert sich, eine zustandsbehaftete Funktion ohne dieses Präfix auszuführen
    • Es löst den automatischen Import aus react aus
  2. Zwei Komponenten rufen useLocalStorage mit demselben Schlüssel auf. Teilen sie ihren Zustand?
    • Ja: Der Hook synchronisiert automatisch alle seine Aufrufer
    • Nein, und die Daten werden unter zwei verschiedenen Speicherschlüsseln abgelegt
    • Nein: Jeder Aufruf besitzt seinen eigenen React-Zustand; nur die Daten in localStorage werden geteilt
  3. Welche Rolle spielt in useDebouncedValue die von useEffect zurückgegebene Aufräumfunktion?
    • Den ausstehenden Timer abbrechen, damit nach der Verzögerung nur der zuletzt getippte Wert veröffentlicht wird
    • Den entprellten Wert bei jedem Unmounten der Komponente zurücksetzen
    • Die erneuten Renderings der Komponente für die gesamte Dauer der Verzögerung blockieren