استخرج المنطق ذا الحالة إلى خطّافات قابلة لإعادة الاستخدام والتركيب والاختبار.
افتح هذا الدرس في Kodokonالخطّاف المخصص دالة يبدأ اسمها بـ use وتستدعي خطّافات أخرى. يستخرج المنطق ذا الحالة، لا الحالة المشتركة: كل مكوّن يستدعيه يحصل على useState و useEffect خاصين به، معزولين تمامًا. إنه أداة React الأكثر مباشرة لتحليل المنطق - فحيث أضافت HOCs و render props طبقات إلى الشجرة، ينزلق الخطّاف إلى جسم المكوّن، ويُركَّب بسهولة، ويسهل اختباره.
import { useEffect, useState } from "react";
function useDebouncedValue(value, delay = 300) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = setTimeout(() => {
setDebounced(value);
}, delay);
return () => clearTimeout(id);
}, [value, delay]);
return debounced;
}بادئة use ليست زخرفية: هي ما يتيح لإضافة ESLint المسماة react-hooks التحقق من قواعد الخطّافات داخل دالتك - استدعاءات غير مشروطة، في المستوى الأعلى. انتبه أيضًا لشكل الواجهة (API) المُرجَعة: صف [value, setValue] عندما يعكس الاستخدام useState، وكائن بمفاتيح مسمّاة بمجرد أن يكون هناك أكثر من شيئين لكشفهما.
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const stored = localStorage.getItem(key);
return stored !== null
? JSON.parse(stored)
: initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}الخطّافات تُركَّب: يمكن لخطّاف أن يستدعي خطّافات أخرى، بما فيها خطّافاتك أنت. هكذا تنشأ تجريدات أعمال قابلة للقراءة، مبنية على لبنات عامة. لاحظ اقتران AbortController بدالة التنظيف: دونه، قد يستجيب طلب أُطلق لإدخال أقدم بعد الأحدث ويطمس نتيجته - حالة السباق (race condition) الكلاسيكية في حقول البحث.
function useUserSearch(query) {
const debounced = useDebouncedValue(query, 400);
const [results, setResults] = useState([]);
useEffect(() => {
if (debounced === "") {
setResults([]);
return;
}
const controller = new AbortController();
fetch("/api/search?q=" + debounced, {
signal: controller.signal,
})
.then((res) => res.json())
.then(setResults)
.catch(() => {});
return () => controller.abort();
}, [debounced]);
return results;
}