Wenn Sie schnell auf Benutzeraktionen reagieren und die neuesten Daten vom Backend abrufen müssen, benötigen Sie möglicherweise einen React Hook, der sequentielle Anfragen unterstützt. Dieser Hook kann frühere Anfragen abbrechen, wenn sie noch laufen, und nur die neuesten Daten zurückgeben. Dies verbessert nicht nur die Leistung, sondern verbessert auch das Benutzererlebnis.
Beginnen wir mit der Erstellung eines einfachen sequentiellen Request-React-Hooks:
import { useCallback, useRef } from 'react'; const buildCancelableFetch = <T>( requestFn: (signal: AbortSignal) => Promise<T>, ) => { const abortController = new AbortController(); return { run: () => new Promise<T>((resolve, reject) => { if (abortController.signal.aborted) { reject(new Error('CanceledError')); return; } requestFn(abortController.signal).then(resolve, reject); }), cancel: () => { abortController.abort(); }, }; }; function useLatest<T>(value: T) { const ref = useRef(value); ref.current = value; return ref; } export function useSequentialRequest<T>( requestFn: (signal: AbortSignal) => Promise<T>, ) { const requestFnRef = useLatest(requestFn); const currentRequest = useRef<{ cancel: () => void } | null>(null); return useCallback(async () => { if (currentRequest.current) { currentRequest.current.cancel(); } const { run, cancel } = buildCancelableFetch(requestFnRef.current); currentRequest.current = { cancel }; return run().finally(() => { if (currentRequest.current?.cancel === cancel) { currentRequest.current = null; } }); }, [requestFnRef]); }
Die Schlüsselidee hier stammt aus dem Artikel „Wie man Versprechen in JavaScript annulliert.“ Sie können es so verwenden:
import { useSequentialRequest } from './useSequentialRequest'; export function App() { const run = useSequentialRequest((signal: AbortSignal) => fetch('http://localhost:5000', { signal }).then((res) => res.text()), ); return <button onClick={run}>Run</button>; }
Auf diese Weise erhalten Sie, wenn Sie mehrmals schnell auf die Schaltfläche klicken, nur die Daten der letzten Anfrage und vorherige Anfragen werden verworfen.
Wenn wir einen umfassenderen React Hook für sequentielle Anfragen benötigen, gibt es im obigen Code Raum für Verbesserungen. Zum Beispiel:
Wir können die Erstellung eines AbortControllers verschieben, bis er tatsächlich benötigt wird, wodurch unnötige Erstellungskosten reduziert werden.
Wir können Generika verwenden, um jede Art von Anforderungsargumenten zu unterstützen.
Hier ist die aktualisierte Version:
import { useCallback, useRef } from 'react'; function useLatest<T>(value: T) { const ref = useRef(value); ref.current = value; return ref; } export function useSequentialRequest<Args extends unknown[], Data>( requestFn: (signal: AbortSignal, ...args: Args) => Promise<Data>, ) { const requestFnRef = useLatest(requestFn); const running = useRef(false); const abortController = useRef<AbortController | null>(null); return useCallback( async (...args: Args) => { if (running.current) { abortController.current?.abort(); abortController.current = null; } running.current = true; const controller = abortController.current ?? new AbortController(); abortController.current = controller; return requestFnRef.current(controller.signal, ...args).finally(() => { if (controller === abortController.current) { running.current = false; } }); }, [requestFnRef], ); }
Beachten Sie, dass wir im „finally“-Block prüfen, ob der aktuelle Controller gleich „abortController.current“ ist, um Race-Conditions zu verhindern. Dadurch wird sichergestellt, dass nur die aktive Anforderung den Ausführungsstatus ändern kann.
Umfassendere Nutzung:
import { useState } from 'react'; import { useSequentialRequest } from './useSequentialRequest'; export default function Home() { const [data, setData] = useState(''); const run = useSequentialRequest(async (signal: AbortSignal, query: string) => fetch(`/api/hello?query=${query}`, { signal }).then((res) => res.text()), ); const handleInput = async (queryStr: string) => { try { const res = await run(queryStr); setData(res); } catch { // ignore errors } }; return ( <> <input placeholder="Please input" onChange={(e) => { handleInput(e.target.value); }} /> <div>Response Data: {data}</div> </> ); }
Sie können es online ausprobieren: Während Sie schnell tippen, werden die vorherigen Anfragen storniert und nur die letzte Antwort angezeigt.
Wenn Sie dies hilfreich fanden, Bitte denken Sie darüber nach, meinen Newsletter zu abonnieren, um weitere nützliche Artikel und Tools zur Webentwicklung zu erhalten. Danke fürs Lesen!
Das obige ist der detaillierte Inhalt vonSo erstellen Sie einen Reaktions-Hook, der sequentielle Anforderungen verarbeitet. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!