Lorsque vous devez répondre rapidement aux actions des utilisateurs et récupérer les dernières données du backend, vous aurez peut-être besoin d'un React Hook prenant en charge les requêtes séquentielles. Ce hook peut annuler les demandes précédentes si elles sont toujours en cours et renvoyer uniquement les données les plus récentes. Cela améliore non seulement les performances, mais améliore également l'expérience utilisateur.
Commençons par créer un hook React de requête séquentielle simple :
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]); }
L'idée clé ici vient de l'article « Comment annuler des promesses en JavaScript ». Vous pouvez l'utiliser comme ceci :
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>; }
De cette façon, lorsque vous cliquez rapidement plusieurs fois sur le bouton, vous n'obtiendrez que les données de la dernière demande et les demandes précédentes seront rejetées.
Si nous avons besoin d’une requête séquentielle plus complète React Hook, il y a place à amélioration dans le code ci-dessus. Par exemple :
Nous pouvons différer la création d'un AbortController jusqu'à ce qu'il soit réellement nécessaire, réduisant ainsi les coûts de création inutiles.
Nous pouvons utiliser des génériques pour prendre en charge tout type d'arguments de requête.
Voici la version mise à jour :
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], ); }
Notez que dans le bloc enfin, nous vérifions si le contrôleur actuel est égal à abortController.current pour éviter les conditions de concurrence. Cela garantit que seule la requête active peut modifier l'état d'exécution.
Utilisation plus complète :
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> </> ); }
Vous pouvez l'essayer en ligne : au fur et à mesure que vous tapez rapidement, les demandes précédentes seront annulées et seule la dernière réponse sera affichée.
Si vous avez trouvé cela utile, veuillez envisager de vous abonner à ma newsletter pour des articles et des outils plus utiles sur le développement Web. Merci d'avoir lu !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!