Cuando necesite responder rápidamente a las acciones del usuario y obtener los datos más recientes del backend, es posible que necesite un React Hook que admita solicitudes secuenciales. Este enlace puede cancelar solicitudes anteriores si aún están en curso y solo devolver los datos más recientes. Esto no solo mejora el rendimiento sino que también mejora la experiencia del usuario.
Comencemos construyendo un gancho de React de solicitud secuencial simple:
import { useCallback, useRef } from 'react'; const buildCancelableFetch =( requestFn: (signal: AbortSignal) => Promise , ) => { const abortController = new AbortController(); return { run: () => new Promise ((resolve, reject) => { if (abortController.signal.aborted) { reject(new Error('CanceledError')); return; } requestFn(abortController.signal).then(resolve, reject); }), cancel: () => { abortController.abort(); }, }; }; function useLatest (value: T) { const ref = useRef(value); ref.current = value; return ref; } export function useSequentialRequest ( requestFn: (signal: AbortSignal) => Promise , ) { const requestFnRef = useLatest(requestFn); const currentRequest = useRef 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]); }
La idea clave aquí proviene del artículo "Cómo anular promesas en JavaScript". Puedes usarlo así:
import { useSequentialRequest } from './useSequentialRequest'; export function App() { const run = useSequentialRequest((signal: AbortSignal) => fetch('http://localhost:5000', { signal }).then((res) => res.text()), ); return ; }
De esta manera, cuando hagas clic en el botón rápidamente varias veces, solo obtendrás los datos de la última solicitud y las solicitudes anteriores se descartarán.
Si necesitamos un React Hook de solicitud secuencial más completo, hay margen de mejora en el código anterior. Por ejemplo:
Podemos posponer la creación de un AbortController hasta que sea realmente necesario, lo que reduce los costos de creación innecesarios.
Podemos usar genéricos para respaldar cualquier tipo de argumentos de solicitud.
Aquí está la versión actualizada:
import { useCallback, useRef } from 'react'; function useLatest(value: T) { const ref = useRef(value); ref.current = value; return ref; } export function useSequentialRequest ( requestFn: (signal: AbortSignal, ...args: Args) => Promise, ) { const requestFnRef = useLatest(requestFn); const running = useRef(false); const abortController = useRef (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], ); }
Tenga en cuenta que en el bloque finalmente, verificamos si el controlador actual es igual a abortController.current para evitar condiciones de carrera. Esto garantiza que solo la solicitud activa pueda modificar el estado de ejecución.
Uso más completo:
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 ( { handleInput(e.target.value); }} />Response Data: {data}> ); }
Puedes probarlo online: a medida que escribas rápidamente, las solicitudes anteriores se cancelarán y solo se mostrará la última respuesta.
Si esto te resultó útil, considera suscribirte a mi boletín para obtener más artículos y herramientas útiles sobre desarrollo web. ¡Gracias por leer!
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3