عندما تحتاج إلى الاستجابة بسرعة لإجراءات المستخدم وجلب أحدث البيانات من الواجهة الخلفية، فقد تحتاج إلى React Hook الذي يدعم الطلبات التسلسلية. يمكن لهذا الخطاف إلغاء الطلبات السابقة إذا كانت لا تزال مستمرة ويعيد فقط أحدث البيانات. وهذا لا يؤدي إلى تحسين الأداء فحسب، بل يعزز أيضًا تجربة المستخدم.
لنبدأ ببناء طلب تسلسلي بسيط، خطاف التفاعل:
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]); }
الفكرة الأساسية هنا تأتي من مقالة "كيفية إلغاء الوعود في جافا سكريبت". يمكنك استخدامه مثل هذا:
import { useSequentialRequest } from './useSequentialRequest'; export function App() { const run = useSequentialRequest((signal: AbortSignal) => fetch('http://localhost:5000', { signal }).then((res) => res.text()), ); return ; }
بهذه الطريقة، عند النقر فوق الزر بسرعة عدة مرات، ستحصل فقط على البيانات من الطلب الأخير، وسيتم تجاهل الطلبات السابقة.
إذا كنا بحاجة إلى React Hook لطلب تسلسلي أكثر شمولاً، فهناك مجال للتحسين في الكود أعلاه. على سبيل المثال:
يمكننا تأجيل إنشاء AbortController حتى تكون هناك حاجة إليه فعليًا، مما يقلل تكاليف الإنشاء غير الضرورية.
يمكننا استخدام الأدوية العامة لدعم أي نوع من وسائط الطلب.
إليك الإصدار المحدث:
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], ); }
لاحظ أنه في الكتلة الأخيرة، نتحقق مما إذا كانت وحدة التحكم الحالية تساوي abortController.current لمنع حالات السباق. وهذا يضمن أن الطلب النشط فقط يمكنه تعديل حالة التشغيل.
استخدام أكثر شمولاً:
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}> ); }
يمكنك تجربتها عبر الإنترنت: أثناء الكتابة بسرعة، سيتم إلغاء الطلبات السابقة، وسيتم عرض الرد الأخير فقط.
إذا وجدت هذا مفيدًا، يرجى الاشتراك في في النشرة الإخبارية الخاصة بي للحصول على المزيد من المقالات والأدوات المفيدة حول تطوير الويب. شكرا على القراءة!
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3