Mit React Hooks können Sie Zustände und andere React-Funktionen nutzen, ohne eine Klasse zu schreiben.
Bevor sie eingeführt wurden, musste man eine Klassenkomponente schreiben, um eine Komponente zu haben, die einen Zustand irgendeiner Art hält. Mit Hilfe von useState, useEffect, useRef und anderen sehr nützlichen eingebauten React Hooks können Sie jetzt Ihre gesamte Anwendung mit einem rein funktionalen Ansatz schreiben! Ist das nicht klasse?
Ich denke, das ist er! Aber das ist noch nicht alles: Neben den eingebauten Hooks gibt es eine Funktion, mit der Sie Ihre eigenen Hooks erstellen können, die im Grunde alles tun können, was Sie brauchen.
In diesem Artikel möchte ich Ihnen ein paar einfache Beispiele für benutzerdefinierte Hooks zeigen, die Ihnen das Leben leichter machen können.
Hook no. 1: useUpdateEffect
Der erste und wahrscheinlich mein Lieblings-Hook heißt useUpdateEffect. Es verhält sich sehr ähnlich wie useEffect, aber es gibt einen großen Unterschied. Sehen Sie, der eingebaute useEffect wird jedes Mal ausgeführt, wenn sich eine Eigenschaft im Abhängigkeitsarray ändert, aber auch beim ersten Rendering der Komponente.
Manchmal wollen wir das verhindern. Zum Beispiel, um diese lästigen Rerender zu reduzieren, die unsere App verlangsamen. Oder vielleicht müssen Sie die API-Daten nicht bei jedem Rerender abrufen, sondern nur, wenn sich ein bestimmter Zustand ändert? Hier ist eine Lösung für dieses Problem:
Wir verwenden useRef, um die isInitialMount-Referenz zu verfolgen und sie im useEffect zu überprüfen. Wir können eine beliebige Callback-Funktion zusammen mit einem Abhängigkeits-Array an unseren benutzerdefinierten Hook übergeben.
import { useEffect, useRef } from 'react';
export default function useUpdateEffect(effect: Function, dependencyArray: Array<any> = []) {
const isInitialMount = useRef(true);
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
} else {
return effect();
}
}, dependencyArray);
}
Hier ist ein einfacher Anwendungsfall für diesen Hook: Wir haben eine Komponente, die einen Wert anzeigt, und wenn sich dieser Wert ändert, wird ein Benutzer benachrichtigt. Wenn wir dort den normalen useEffect verwenden würden, würde jedes Mal, wenn die Komponente neu gerendert wird, ein Warndialog erscheinen. Jetzt ist es nur noch wichtig, dass sich der Wert ändert. Klasse!
import { useUpdateEffect } from 'hooks/useUpdateEffect';
export const myComponent = (props) => {
const { value } = props;
useUpdateEffect(() => {
alert(`Value has changed to: ${value}`);
}, [value]);
return ( <span> {value} </span> );
};
Der nächste Haken ist ebenfalls ein einfacher. Er ist in vielen Situationen nützlich, in denen wir wissen möchten, was der vorherige Wert des Zustands in unserer Komponente war.
In klassenbasierten Komponenten hatten wir eine Lebenszyklusmethode componentDidUpdate, die uns den vorherigen Zustand lieferte. Um diese Funktionalität in funktionalen Komponenten zu haben, können wir unseren eigenen benutzerdefinierten Hook bauen, um dieses Szenario für uns zu handhaben.
Haken Nr. 2: usePrevious-Haken
import { useRef } from "react"
export const usePrevious = (value) => {
const currentRef = useRef(value)
const prevRef = useRef()
if (currentRef.current !== value) {
prevRef.current = currentRef.current
currentRef.current = value
}
return prevRef.current
}
Es gibt bestimmte Situationen in der App-Entwicklung, in denen Sie wissen müssen, was der vorherige Statuswert war. Dies ist ein Szenario, in dem der usePrevious-Haken sehr nützlich ist.
Sie müssen nur wie üblich einen Zustand definieren, etwa so:
const [count, setCount] = useState(0);
Dann weisen Sie diesen Statuswert einem usePrevious-Hook zu und schon haben Sie Zugriff auf den alten Zählwert!
const previousCount = usePrevious(count)
Hook no. 3: useTimeout
Dieser Aufhänger ist sehr einfach. Sie unterscheidet sich nicht von der normalen setTimeout-Methode von Vanilla Javascript. Aber dieser benutzerdefinierte Haken vereinfacht die Verwendung und - was am wichtigsten ist - lässt Sie vergessen, das Intervall zu löschen, wenn Sie es nicht mehr brauchen.
Das Verhalten ist super einfach - Sie rufen useTimeout auf, mit einer Callback-Methode als erstem Parameter und Sie geben die Zeit in Millisekunden als zweiten Parameter an. Der Rest wird für Sie erledigt.
Sie erhalten nicht mehr diese lästige Fehlermeldung "[...]can't perform a React state update on an unmounted component[...]", wenn Sie versehentlich vergessen, clearTimeout im cleanup-Teil von useEffect auszuführen.
import { useCallback, useEffect, useRef } from 'react';
export const useTimeout = (callback, timeoutDelay) => {
const callbackRef = useRef(callback);
const timeoutRef = useRef();
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
const set = useCallback(() => {
timeoutRef.current = setTimeout(() => callbackRef.current(), timeoutDelay);
}, [timeoutDelay]);
const clear = useCallback(() => {
timeoutRef.current && clearTimeout(timeoutRef.current);
}, []);
useEffect(() => {
set();
return clear;
}, [timeoutDelay, set, clear]);
const reset = useCallback(() => {
clear();
set();
}, [clear, set]);
return { reset, clear };
};
Hook no. 4: useDebounce
Dieser Haken kann sich als nützlich erweisen, wenn Sie versuchen, Suchfunktionen in Ihre Anwendung zu implementieren.
Ein Beispiel: Sie versuchen, eine API aufzurufen, die ein Suchergebnis auf der Grundlage Ihrer Texteingabe zurückgibt. Aber jedes Mal, wenn Sie eine Taste auf Ihrer Tastatur drücken, einen API-Aufruf zu tätigen, kann einige Probleme verursachen. Wenn Sie nach einem bestimmten Schlüsselwort suchen, können Sie tatsächlich warten, bis ein Nutzer mit dem Tippen fertig ist oder zumindest für eine bestimmte Zeit aufhört. Hier kommt die Entschärfung ins Spiel.
Der Begriff "Entprellen" kommt aus der Elektronik - es ist ein Prozess, der passiert, wenn Sie eine Taste drücken, zum Beispiel auf Ihrer TV-Fernbedienung, das Signal wandert zum Mikrocontroller der Fernbedienung so schnell, dass, bevor Sie die Taste loslassen, es springt, und der Prozessor registriert Ihren Klick mehrmals.
Um dieses Problem zu bekämpfen, stoppt der Mikrocontroller die Registrierung von Aktionen für den Bruchteil einer Sekunde, um zu verhindern, dass mehrere Signale umsonst gesendet werden.
Das Gleiche gilt für die JavaScript-Welt. Ein Beispiel für ein Suchfeld zeigt uns, dass wir eine bestimmte Methode nur einmal pro festgelegter Zeit aufrufen möchten.
Umsetzung:
In diesem Fall werden wir eine "Hookception" durchführen. Wir werden unseren eigenen benutzerdefinierten Haken verwenden, um einen weiteren benutzerdefinierten Haken zu erstellen.
Mit useTimeout ist es super einfach - wir müssen nur unsere reset- und clear-Methoden holen, unser Timeout mit einer Callback-Funktion setzen und die Verzögerung einstellen, die aus den Parametern der useDebounce-Methode stammt, und danach aufräumen.
import useTimeout from 'hooks/useTimeout';
export const useDebounce = (callback, delay, dependencies) => {
const { reset, clear } = useTimeout(callback, delay);
useEffect(reset, [...dependencies, reset]);
useEffect(clear, []);
};
Hook no. 5: useStorage
Während Ihrer Frontend-Entwicklung Karriere haben Sie entweder bereits oder werden Sie über Browser Storage stolpern. Lokaler Speicher oder Sitzungsspeicher. Beide haben ihre Anwendungsfälle, Vorteile und Grenzen.
Unsere nächste Hook-Implementierung wird die Interaktion mit beiden Arten der Speicherung vereinfachen.
Die Werte werden als Key -> Value Mapping im JSON-Format gespeichert. Er vereinfacht das Erhalten und Speichern von Werten.
Unsere Implementierung wird sich darum kümmern, Werte aus dem Speicher zu holen und sie zu entfernen.
Dieser Hook liefert ein Array mit einem Speicherwert, eine Methode zum Speichern des Wertes und eine Funktion, die für das Entfernen des Wertes aus dem Speicher verantwortlich ist.
import { useCallback, useState, useEffect } from 'react';
export const useLocalStorage = (key, value) => useStorage(key, value, window.localStorage);
export const useSessionStorage = (key, value) => {
return useStorage(key, value, window.sessionStorage);
}
const useStorage = (key, value, storageObject) => {
const [storageValue, setStorageValue] = useState(() => {
const jsonValue = storageObject.getItem(key);
if (jsonValue != null) return JSON.parse(jsonValue);
if (typeof value === 'function') {
return value();
}
return value;
});
useEffect(() => {
if (storageValue === undefined) return storageObject.removeItem(key);
storageObject.setItem(key, JSON.stringify(storageValue));
}, [key, storageValue, storageObject]);
const remove = useCallback(() => {
setStorageValue(undefined);
}, []);
return [storageValue, setStorageValue, remove];
}
Um diesen Hook zu verwenden, müssen wir ihn nur aufrufen und die zurückgegebenen Werte und Methoden umstrukturieren.
const [user, setUser, removeUser] = useSessionStorage("user", "John Doe")
Mit benutzerdefinierten Hooks können wir bestimmte Teile unserer Anwendung abstrahieren, um unseren Code sauberer und leichter lesbar zu machen und die Logik in unserer Anwendung wiederzuverwenden.
Geschäftslogik ist auch ein guter Kandidat, um in benutzerdefinierte Hooks eingebaut zu werden. Nicht nur, dass unser Code leichter zu pflegen ist - es ist besser, nur an einer Stelle zu prüfen, was mit der Logik nicht stimmt, als es über 10 Komponenten zu verteilen. Aber es bringt uns auch näher an reine, wiederverwendbare Komponenten als Basis für unsere App. Es besteht keine Notwendigkeit mehr, spezielle Komponenten zu haben, die sich mit unserer Geschäftslogik befassen.
Beitrag von Jacek Paciorek
GitHub
Portfolio
LinkedIn