11.7-bob: Custom hooks (o'z hooklaringni yozish)
11-QISM — Frontend: React · 7-mavzu
1. Kirish va motivatsiya
11.5 va 11.6-boblarda React'ning o'rnatilgan (built-in) hooklarini — useState, useEffect, useContext, useRef, useReducer, useMemo, useCallback — chuqur o'rgandik. Endi React'ning eng nafis va eng kuchli imkoniyatiga o'tamiz: o'z hooklaringni yozish — custom hooks. Bu — hooklar inqilobining asl maqsadi edi. Class komponentlar davrida bir komponentdagi mantiqni (masalan "oynaning kengligini kuzatish" yoki "serverdan ma'lumot olish") boshqasiga qayta ishlatish juda og'riqli edi — buning uchun HOC (Higher-Order Component) yoki render props kabi murakkab, ichma-ich joylashtiruvchi naqshlar ishlatilardi ("wrapper hell"). Custom hooklar bu muammoni butunlay hal qildi: stateful mantiqni oddiy funksiyaga ajratasiz va uni istalgan joyda qayta ishlatasiz.
Eng yaxshi xabar — custom hook yozish uchun yangi hech narsa o'rganishing shart emas. Custom hook — bu shunchaki use bilan boshlanadigan, ichida boshqa hooklarni chaqiradigan oddiy funksiya. Siz allaqachon bilgan useState, useEffect'ni shu funksiya ichiga solib, uni useFetch, useLocalStorage, useToggle deb nomlaysiz — tamom, bu custom hook. Lekin oddiy ko'rinish ortida chuqur g'oya yotadi: custom hooklar mantiqni (logikani) ulashadi, state'ni emas — har bir chaqiruv o'zining mustaqil state'ini oladi. Bu nozik, lekin hal qiluvchi tushunchani aniq anglamasdan custom hooklarni noto'g'ri ishlatish mumkin.
Bu bob: mantiqni qayta ishlatish muammosi (HOC/render props tarixi custom hook yechimi), custom hook nima (use* funksiya, hooklarni chaqiradi), qoidalar (use prefiksi nega majburiy, Rules of Hooks amal qiladi), eng muhim tushuncha — custom hook logikani ulashadi, state'ni emas (har chaqiruv izolyatsiyalangan), komponentdan mantiqni ajratish jarayoni, qaytarish konvensiyasi (massiv vs obyekt), parametrlash (argument va deps), hooklarni birlashtirish (composition), va 20+ amaliy custom hook (useToggle, useCounter, usePrevious, useLocalStorage, useDebounce, useThrottle, useFetch (AbortController + refetch), useWindowSize, useOnClickOutside, useMediaQuery, useInterval, useEventListener, useCopyToClipboard, useIntersectionObserver, useAsync, useDisclosure, useForm, useAuth), hook vs oddiy util funksiya chegarasi, TypeScript bilan custom hook (generic, as const), sinash (renderHook), qachon ajratish kerakligi, va tashqi kutubxonalar (usehooks-ts, react-use, ahooks). Custom hooklarni amaliy kutubxona darajasida — mexanizmigacha — ochamiz.
O'xshatish: Custom hook — bu oshxonadagi o'z retseptlaringiz. React'ning built-in hooklari (
useState,useEffect) — bular xom mahsulotlar (un, tuxum, yog'). Har safar "tort" pishirmoqchi bo'lganda, xom mahsulotlardan boshlash zerikarli va xatoga moyil. Shuning uchun bir marta retsept (custom hook) yoziladi — "useFetch: mana shunday qilib serverdan ma'lumot olinadi" — va keyin u butun ilovada (va boshqa loyihalarda) qayta ishlatiladi. Eng muhimi: retseptni ikki kishi ishlatsa, ikkita alohida tort chiqadi (bir-biriga aralashmaydi) — xuddi shunday, bitta custom hook ikki komponentda ishlatilsa, har biri o'zining mustaqil state'ini oladi. Retsept — bu jarayon (logika), tayyor tort emas (state). Va tajribali oshpazlar tayyor retsept kitoblaridan (usehooks-ts) ham foydalanadi — har narsani noldan yozmaydi.
Nega muhim?
- DRY — bir marta yozilgan mantiq (fetch, localStorage, debounce) butun loyihada qayta ishlatiladi 9.1-bob.
- Toza komponentlar — murakkab mantiqni hookga ajratsangiz, komponent faqat UI'ga qaraydi (o'qish oson).
- Sinash oson — custom hook alohida sinaladi (komponentdan mustaqil).
- Senior ko'nikma — yaxshi custom hook dizayni — toza React arxitekturasining belgisi; har real loyihada bor.
2. Nazariya — chuqur tushuntirish
2.1. Mantiqni qayta ishlatish muammosi (HOC/render props custom hook)
MUAMMO: ikki komponent BIR XIL mantiqni ishlatadi (masalan oyna kengligini kuzatish)
mantiqni qanday ULASHISH (komponentlar orasida)?
ESKI YECHIM 1 — HOC (Higher-Order Component) — komponentni o'rovchi funksiya:
const Enhanced = withWindowSize(MyComponent); // o'rab, props sifatida beradi
"wrapper hell": withAuth(withTheme(withData(Component))) — ichma-ich, chalkash
ESKI YECHIM 2 — RENDER PROPS — funksiya prop orqali:
<WindowSize>{width => <div>{width}</div>}</WindowSize>
JSX'da ichma-ich joylashish ("pyramid of doom"), o'qish qiyin
YANGI YECHIM — CUSTOM HOOK (oddiy funksiya):
const width = useWindowSize(); // toza, yassi, ichma-ich emas, o'qish oson
┌──────────────────────────────────────────────────────────┐
│ HOC/render props: mantiqni ULASHISH uchun KOMPONENT qatlam│
│ Custom hook: mantiqni ULASHISH uchun shunchaki FUNKSIYA │
└──────────────────────────────────────────────────────────┘
Custom hook — "wrapper hell"siz mantiq ulashish (hooklarning asl maqsadi)Mantiqni qayta ishlatish muammosi — custom hooklarning tug'ilish sababi. Ko'p komponent bir xil stateful mantiqni ishlatishi mumkin (oyna kengligini kuzatish, ma'lumot olish, forma boshqarish) — savol: bu mantiqni komponentlar orasida qanday ulashish? Hooklardan oldin ikki yechim bor edi: (1) HOC (Higher-Order Component) — komponentni o'rab, qo'shimcha props beradigan funksiya (
withWindowSize(Component)); muammosi — ko'p HOC ichma-ich joylansa "wrapper hell" (withAuth(withTheme(withData(C)))— chalkash, debug qiyin). (2) Render props — funksiya prop orqali mantiq ulashish (<WindowSize>{w => ...}</WindowSize>); muammosi — JSX'da ichma-ich "piramida" (o'qish qiyin). Custom hook ikkalasini ham yengadi: mantiqni ulashish uchun komponent qatlami emas, shunchaki oddiy funksiya —const width = useWindowSize()(yassi, toza, ichma-ich emas). Bu — hooklarning asl maqsadi: stateful mantiqni "wrapper hell"siz, oddiy funksiya chaqiruvidek qayta ishlatish. HOC va render props hali ham mavjud 11.13-bob, lekin mantiq ulashish uchun custom hook — zamonaviy, afzal yo'l.
2.2. Custom hook nima — use* funksiya
CUSTOM HOOK — "use" bilan boshlanadigan, ichida HOOK chaqiradigan oddiy funksiya:
function useToggle(initial = false) { // nom "use" bilan
const [value, setValue] = useState(initial); // ichida built-in hook
const toggle = () => setValue(v => !v);
return [value, toggle]; // kerakli narsani qaytaradi
}
ISHLATISH (oddiy funksiya chaqiruvidek):
const [isOpen, toggleOpen] = useToggle(false);
CUSTOM HOOK = oddiy funksiya + bitta SHART: ichida hook ishlatadi
agar ichida hook (useState/useEffect/...) bo'lmasa — bu oddiy yordamchi funksiya (use kerak emas)
┌──────────────────────────────────────────────────────────┐
│ Oddiy funksiya: hisob qiladi (formatDate, sum) — "use" yo'q│
│ Custom hook: ichida hook bor (state/effect) — "use" SHART │
└──────────────────────────────────────────────────────────┘
Yangi sintaksis YO'Q — bilgan hooklarni funksiyaga solib, "use" deb nomlash yetarliCustom hook —
usebilan boshlanadigan, ichida bir yoki bir nechta hook chaqiradigan oddiy JavaScript funksiyasi. Misol:useToggle— ichidauseStateishlatadi,togglefunksiyasi yaratadi, va[value, toggle]qaytaradi; uniconst [isOpen, toggleOpen] = useToggle()deb ishlatasiz — xuddi built-in hookdek. Eng muhim haqiqat: yangi sintaksis yo'q — siz allaqachon bilgan hooklarni (useState,useEffect) shunchaki funksiyaga solasiz va uniuse* deb nomlaysiz. Custom hook = oddiy funksiya + bitta shart: ichida hook ishlatadi. Agar funksiya ichida hook bo'lmasa (faqat hisob qiladi —formatDate,sum), bu custom hook emas, oddiy yordamchi funksiya (useprefiksini ishlatma). Demak chegara aniq: ichidauseState/useEffect/boshqa hook bormi? Ha custom hook (use*); yo'q oddiy funksiya. Bu soddalik — custom hooklarning kuchi: o'rganish uchun yangi narsa yo'q, faqat tashkillash usuli.
2.3. Custom hook qoidalari — use prefiksi va Rules of Hooks
QOIDA 1 — nom "use" bilan boshlansin (use + bosh harf): useToggle, useFetch
NEGA: React (va ESLint) "use*" ni hook deb taniydi Rules of Hooks'ni tekshiradi
"use"siz nomda (getToggle) ESLint hookni ko'rmaydi qoida buzilishini sezmaydi (xatar)
QOIDA 2 — Rules of Hooks amal qiladi (11.5: 2.2):
- custom hook FAQAT komponent yoki boshqa hook ichida chaqiriladi
- custom hook ENG YUQORIDA chaqiriladi (shart/tsikl ichida emas)
if (x) { const v = useToggle(); } // shart ichida — taqiq
const v = useToggle(); if (x) {...} // tepada
QOIDA 3 — custom hook ICHIDA hooklar ham Rules'ga bo'ysunadi (tepada, shartsiz)
┌──────────────────────────────────────────────────────────┐
│ "use" prefiksi — shunchaki kelishuv EMAS, ESLint signali │
│ React Hooks ESLint plugin "use*"ni hook deb tekshiradi │
└──────────────────────────────────────────────────────────┘
"use" bilan boshlanmasa — hook xatolarini ESLint TUTMAYDI (jim buglar)Custom hook qoidalari — uchta. Qoida 1 — nom
usebilan boshlansin (useToggle,useFetch): bu shunchaki kelishuv emas, texnik zaruriyat — React vaeslint-plugin-react-hooksfunksiyaniuseprefiksiga qarab hook deb taniydi va unga Rules of Hooks'ni qo'llaydi; agarusesiz nomlansa (getToggle), ESLint uni oddiy funksiya deb biladi va hook qoidalari buzilishini sezmaydi (jim, qiyin topiladigan buglar). Qoida 2 — Rules of Hooks amal qiladi (11.5: 2.2): custom hook faqat komponent yoki boshqa hook ichida, va eng yuqorida (shart/tsikl ichida emas) chaqiriladi — chunki u ichida hook ishlatadi, demak chaqiruv tartibi muhim. Qoida 3 — custom hook ichidagi hooklar ham Rules'ga bo'ysunadi (tepada, shartsiz). Asosiy xulosa:useprefiksi — ESLint uchun signal; usiz yozsangiz, hook qoidasi buzilishini avtomatik tekshiruv tutmaydi. Shuning uchun ichida hook bo'lgan har funksiya majburanuse*deb nomlanishi kerak.
2.4. Custom hook LOGIKANI ulashadi, STATE'ni emas (eng muhim)
ENG NOZIK TUSHUNCHA: custom hook MANTIQNI ulashadi, STATE'ni EMAS
Ikki komponent bir xil hook ishlatsa — har biri O'Z mustaqil state'ini oladi:
function ComponentA() {
const [count, inc] = useCounter(); // count_A — ALOHIDA
}
function ComponentB() {
const [count, inc] = useCounter(); // count_B — ALOHIDA (A'ga aloqasi yo'q)
}
// ComponentA'da inc qilsang faqat count_A oshadi (count_B o'zgarmaydi)
┌────────────────────────────────────────────────────────────┐
│ Custom hook — "retsept" (jarayon/logika), tayyor "tort" emas │
│ Har chaqiruv yangi, izolyatsiyalangan state nusxasi │
└────────────────────────────────────────────────────────────┘
AGAR state'ni ULASHISH kerak bo'lsa (A va B BIR XIL count ko'rsin):
custom hook YETMAYDI Context 12.1-bob yoki global store (Redux/Zustand — 12.2, 12.5)
Custom hook = qayta ishlatiladigan MANTIQ; ulashilADIgan STATE emas (chalkashtirma!)Custom hook logikani ulashadi, state'ni emas — bu bobning eng muhim va eng ko'p chalkashtiriladigan tushunchasi. Ikki komponent bir xil custom hookni chaqirsa, ular mantiqni (jarayonni) ulashadi, lekin har biri o'zining mustaqil, izolyatsiyalangan state'ini oladi. Masalan,
ComponentAvaComponentBikkalasiuseCounter()ishlatsa —count_Avacount_Balohida (A'daincqilsangiz faqat A'ning soni oshadi, B'niki tegmaydi). Bu xuddi "retsept" kabi: retsept (hook) — bu jarayon, har kim undan o'z alohida tortini (state) pishiradi. Demak agar siz state'ni ulashishni xohlasangiz (A va B bir xil count ko'rsin), custom hook yetmaydi — buning uchun Context (12.1, ma'lumotni daraxt bo'ylab ulashish) yoki global store (Redux/Zustand — 12.2, 12.5) kerak. Custom hook — bu qayta ishlatiladigan mantiq, ulashiladigan state emas (buni chalkashtirmaslik muhim). Bu farqni aniq tushunish — custom hooklarni to'g'ri ishlatishning va keyingi state-management mavzularining (12-QISM) kalitidir.
2.5. Komponentdan mantiqni ajratish (extraction jarayoni)
CUSTOM HOOK YARATISH — komponentdagi mantiqni "ko'chirish" (extraction):
OLDIN (mantiq komponent ichida — takrorlanadi, komponentni to'ldipadi):
function Profile() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/user").then(r => r.json()).then(d => { setData(d); setLoading(false); });
}, []);
return loading ? <Spinner/> : <div>{data.name}</div>;
}
KEYIN (mantiq useFetch hook'ga ko'chdi — komponent toza):
function useFetch(url) { // 1. mantiqni funksiyaga ko'chir
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url).then(r => r.json()).then(d => { setData(d); setLoading(false); });
}, [url]);
return { data, loading }; // 2. kerakli narsani qaytar
}
function Profile() {
const { data, loading } = useFetch("/api/user"); // 3. komponent faqat UI'ga qaraydi
return loading ? <Spinner/> : <div>{data.name}</div>;
}
Komponent "NIMA ko'rsatish"ga qaraydi; hook "QANDAY olish/hisoblash"ni biladi (ajratish)Komponentdan mantiqni ajratish — custom hook yaratishning amaliy jarayoni. Odatda mantiq avval komponent ichida yoziladi (
useState+useEffectbilan ma'lumot olish), keyin u takrorlana boshlaganda yoki komponentni "to'ldirib" yuborganda — uni custom hookga ko'chirasiz (extraction). Uch qadam: (1) stateful mantiqni (useState/useEffectbloki) yangiuse*funksiyaga ko'chir; (2) komponentga kerakli narsanireturnqil; (3) komponentda uni chaqirib, qaytganini ishlat. Natija: komponent "nima ko'rsatish"ga (UI) qaraydi, hook "qanday olish/hisoblash"ni biladi — mas'uliyat ajratiladi (9.1 SRP). Bu ajratish ikki foyda beradi: (1) komponent toza va o'qish oson bo'ladi (faqat UI mantig'i); (2) ajratilgan mantiq qayta ishlatiladi (boshqa komponentdauseFetch("/api/products")). Custom hook yozishni "boshidan rejalashtirma" — avval komponentda oddiy yoz, takrorlanishni yoki murakkablashishni sezganda ajrat 2.13-bob.
2.6. Qaytarish konvensiyasi — massiv vs obyekt
CUSTOM HOOK kerakli narsani qaytaradi — IKKI usul:
MASSIV [] — kam element (1-2) + chaqiruvchi NOM bermoqchi bo'lsa:
function useToggle() { ... return [value, toggle]; }
const [isOpen, toggleOpen] = useToggle(); // istalgan nom (useState kabi)
const [isDark, toggleDark] = useToggle(); // qayta — boshqa nom (qulay)
OBYEKT {} — ko'p element YOKI ba'zilarini olish kerak bo'lsa:
function useFetch() { ... return { data, loading, error, refetch }; }
const { data, loading } = useFetch(url); // faqat keraklisini ol (tartib muhim emas)
const { data: users } = useFetch(url); // qayta nomlash mumkin
┌──────────────┬────────────────────────────────────────────┐
│ Massiv [] │ 1-2 qiymat, chaqiruvchi NOM beradi (useState)│
│ Obyekt {} │ 3+ qiymat, tanlab olish, o'z-o'zini hujjatlash│
└──────────────┴────────────────────────────────────────────┘
Massiv — pozitsiya bo'yicha (tartib muhim); obyekt — nom bo'yicha (tartib erkin)
Ko'p qiymatli hookda obyekt afzal (data, loading, error — nomi o'zini tushuntiradi)Qaytarish konvensiyasi — custom hook kerakli narsani qaytaradi, ikki usul bilan: massiv
[]yoki obyekt{}. Massiv — kam element (1-2) bo'lganda va chaqiruvchi o'z nomini bermoqchi bo'lganda (xuddiuseStatekabi:const [isOpen, toggleOpen] = useToggle(), keyinconst [isDark, toggleDark] = useToggle()— har chaqiruvda boshqa nom — qulay). Obyekt — ko'p element (3+) bo'lganda yoki faqat ba'zilarini olish kerak bo'lganda (const { data, loading } = useFetch(url)— faqat keraklisini olasiz, tartib muhim emas, kerak bo'lsa qayta nomlaysizdata: users). Farq: massiv — pozitsiya bo'yicha (tartib muhim — birinchi element value, ikkinchi setter), obyekt — nom bo'yicha (tartib erkin, o'zini hujjatlaydi). Amaliy qoida: 1-2 qiymat va nomlash kerak — massiv (useToggle,useCounter); 3+ qiymat (data,loading,error,refetch) — obyekt (nomlari o'zlarini tushuntiradi, tanlab olinadi). Ko'pchilik real custom hooklar (ayniqsa fetch/forma) obyekt qaytaradi.
2.7. Argument va parametrlash — hookni moslashuvchan qilish
CUSTOM HOOK ARGUMENT oladi (oddiy funksiya kabi) — moslashuvchanlik uchun:
function useFetch(url) { // url — argument
useEffect(() => { fetch(url)... }, [url]); // argument deps'da (o'zgarsa qayta — 11.5: 2.6)
}
useFetch("/api/users"); useFetch("/api/products"); // turli url
KO'P ARGUMENT / OPSIYA OBYEKT:
function useFetch(url, { method = "GET", enabled = true } = {}) {
useEffect(() => { if (enabled) fetch(url, { method }); }, [url, method, enabled]);
}
useFetch("/api/users", { enabled: isLoggedIn }); // shartli yuklash
Argument effekt ichida ishlatilsa — deps'ga qo'shish shart (exhaustive-deps — 11.5: 2.6)
Obyekt/funksiya argument — har chaqiruvda yangi havola ehtiyot (useMemo/useCallback — 11.6: 2.9)Argument va parametrlash — custom hook ham oddiy funksiya kabi argument oladi (moslashuvchanlik uchun). Masalan
useFetch(url)—urlni argument sifatida olib, turli endpoint'lar uchun ishlatiladi (useFetch("/api/users"),useFetch("/api/products")). Argument hook ichidagi effektda ishlatilsa, uni dependency array'ga qo'shish shart (}, [url]—urlo'zgarganda qayta ishlasin — 11.5: 2.6). Ko'p moslashuv kerak bo'lsa, opsiya obyekt ishlatiladi:useFetch(url, { method, enabled })— masalanenabled: isLoggedInbilan shartli yuklash. Ikki ehtiyotkorlik: (1) effektda ishlatilgan har argument deps'da bo'lsin (exhaustive-deps— 11.5: 2.6); (2) agar argument obyekt yoki funksiya bo'lsa, u chaqiruvchida har render yangi havola olishi mumkin (11.6: 2.9) — bu hook ichidagi effektni keraksiz qayta ishga tushiradi; bunda chaqiruvchi uniuseMemo/useCallbackbilan barqaror ushlashi yoki hook ichida primitiv qismni dep qilishi kerak. Parametrlash — custom hookni bir marta yozib, ko'p holatda ishlatishning kaliti.
2.8. Hooklarni birlashtirish (composition — hook hookni chaqiradi)
CUSTOM HOOK ichida boshqa CUSTOM HOOK chaqirilishi mumkin (kompozitsiya):
function useDebouncedSearch(query) {
const debouncedQuery = useDebounce(query, 400); // custom hook ichida custom hook
const { data, loading } = useFetch( // yana custom hook
debouncedQuery ? `/api/search?q=${debouncedQuery}` : null
);
return { results: data, loading };
}
// useDebouncedSearch = useDebounce + useFetch (ikkita kichik hook bitta katta)
┌────────────────────────────────────────────────────────────┐
│ Kichik, fokuslangan hooklar birlashtirib murakkab mantiq │
│ (xuddi komponent kompozitsiyasi — 11.2: 2.9 — lekin mantiqda)│
└────────────────────────────────────────────────────────────┘
Kichik hooklar (useDebounce, useFetch) — qayta ishlatiladigan "g'ishtlar"
Ularni birlashtirib (compose) yuqori darajadagi mantiq quriladi (toza, sinaladigan)Hooklarni birlashtirish (composition) — custom hook ichida boshqa custom hook chaqirilishi mumkin, xuddi built-in hookni chaqirgandek. Masalan
useDebouncedSearch— ichidauseDebounce(qiymatni kechiktirish) vauseFetch(ma'lumot olish) ni birlashtiradi:querydebounced fetch natija. Ya'ni ikkita kichik, fokuslangan hook birlashib bitta yuqori darajadagi mantiqni tashkil qiladi. Bu — komponent kompozitsiyasiga (11.2: 2.9) o'xshaydi, lekin UI emas, mantiq darajasida. Bu yondashuvning kuchi: kichik, bitta ishni yaxshi qiladigan hooklar (useDebounce,useFetch,useToggle) — qayta ishlatiladigan "g'ishtlar"; ularni birlashtirib (compose) murakkabroq mantiq quriladi (useDebouncedSearch,useAuth,useInfiniteScroll). Har "g'isht" alohida sinaladi va boshqa joyda qayta ishlatiladi. Bu — toza, modulli React arxitekturasining cho'qqisi: UI komponentlar kompozitsiyasi + mantiq hooklar kompozitsiyasi.
2.9. useToggle va usePrevious — oddiy hooklar
useToggle — boolean almashtirish (modal, accordion, switch):
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue(v => !v), []); // barqaror (11.6: 2.10)
return [value, toggle];
}
usePrevious — qiymatning OLDINGI holatini eslab qolish (useRef — 11.5: 2.13):
function usePrevious(value) {
const ref = useRef();
useEffect(() => { ref.current = value; }, [value]); // har render'dan keyin joriy'ni sakla
return ref.current; // effektdan OLDIN o'qiladi oldingi qiymat
}
// const prevCount = usePrevious(count); // count o'zgarishidan oldingisi
usePrevious — useRef'ning klassik ishlatilishi (render'siz "oldingi" qiymat — 11.5: 2.13)
useTogglevausePrevious— eng oddiy, lekin eng ko'p ishlatiladigan custom hooklar.useToggle— boolean qiymatni almashtirish (modal ochish/yopish, accordion, switch): ichidauseState+togglefunksiya (updaterv => !v— 11.4: 2.6, vauseCallbackbilan barqaror — 11.6: 2.10),[value, toggle]qaytaradi.usePrevious— qiymatning oldingi holatini eslab qolish (masalan animatsiya yoki "nima o'zgardi" uchun):useRefishlatadi (11.5: 2.13) —useEffecthar render'dan keyin joriy qiymatni ref'ga saqlaydi, lekinreturn ref.currenteffektdan oldin o'qiladi, shuning uchun oldingi (avvalgi render'dagi) qiymat qaytariladi.usePrevious—useRefning klassik namunasi (render'siz, ko'rinmaydigan "oldingi qiymat" saqlash). Bu ikki hook kichik bo'lsa-da, custom hook tuzilishining toza namunasi: bilgan built-in hookni (useState/useRef+useEffect) o'rab, qulay nom va interfeys berish.
2.10. useLocalStorage — state + saqlash (persistence)
useLocalStorage — state localStorage'da SAQLANadi (sahifa qayta yuklansa — qoladi):
function useLocalStorage(key, initialValue) {
// 1. Boshlang'ich: localStorage'dan o'qi (lazy init — qimmat, bir marta — 11.5)
const [value, setValue] = useState(() => {
const saved = localStorage.getItem(key);
return saved !== null ? JSON.parse(saved) : initialValue;
});
// 2. value o'zgarganda localStorage'ga yoz (sinxronlash — 11.5: 2.8)
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue]; // useState bilan bir xil interfeys
}
ISHLATISH (useState kabi, lekin saqlanadi):
const [theme, setTheme] = useLocalStorage("theme", "light");
// sahifani yangilab ochsang ham — theme saqlangan qoladi
useState + useEffect(persist) birlashmasi "saqlanadigan state" (eng foydali hook)
JSON.parse/stringify — obyekt/massivni ham saqlash uchun (string emas)
useLocalStorage— eng foydali custom hooklardan biri: state'nilocalStorageda saqlaydi, shuning uchun sahifa qayta yuklansa ham qiymat qoladi (mavzu, til, savat, forma qoralamasi). Ikki qism: (1) boshlang'ich qiymat —useStatening lazy init 11.5-bob bilanlocalStoragedan o'qiladi (faqat birinchi render'da — qimmat o'qishni takrorlamaydi), agar yo'q bo'lsainitialValue; (2) saqlash —useEffectbilanvalueo'zgargandalocalStoragega yoziladi (state'ni tashqi tizim bilan sinxronlash — 11.5: 2.8).JSON.parse/stringify— obyekt/massivni ham saqlash uchun (localStorage faqat string saqlaydi). Qaytarish interfeysiuseStatebilan bir xil ([value, setValue]) — shuning uchunuseStateniuseLocalStoragebilan to'g'ridan almashtirib bo'ladi:const [theme, setTheme] = useLocalStorage("theme", "light"). Bu hook —useState+useEffect(persist) birlashmasining klassik namunasi va custom hooklarning kuchini ko'rsatadi: oddiy funksiya, lekin butun loyihada "saqlanadigan state" muammosini hal qiladi. Yana bir nozik jihat: agar foydalanuvchi ikki tabda bir xil sahifani ochsa, bir tabdagi o'zgarish ikkinchisiga ko'chmaydi — buni qo'shish uchun brauzerningstoragehodisasini tinglash mumkin (window.addEventListener("storage", ...)— boshqa tablocalStorageni o'zgartirganda ishga tushadi). Bu —useEventListener(Misol 15) bilan qulay qo'shiladigan "tablararo sinxronizatsiya" imkoniyati; tayyorusehooks-tsversiyasida 2.14-bob allaqachon bor.
2.11. useFetch va useDebounce — effekt asosli hooklar
useDebounce — qiymatni KECHIKTIRISH (tez o'zgarishda — oxirgisini kutish):
function useDebounce(value, delay = 400) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer); // cleanup: yangi value kelsa eski timerni bekor qil (11.5: 2.7)
}, [value, delay]);
return debounced; // value to'xtagach yangilanadi
}
// const debouncedQuery = useDebounce(query, 400); // har harfda emas, to'xtagach
useFetch — ma'lumot olish (loading/error/data — qayta ishlatiladigan):
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return; // url yo'q bo'lsa — to'xta
let ignore = false; // race himoyasi (11.5: 2.7)
setLoading(true); setError(null);
fetch(url)
.then(r => { if (!r.ok) throw new Error("Xato"); return r.json(); })
.then(d => { if (!ignore) setData(d); })
.catch(e => { if (!ignore) setError(e.message); })
.finally(() => { if (!ignore) setLoading(false); });
return () => { ignore = true; }; // cleanup: eski javobni rad et
}, [url]);
return { data, loading, error };
}
Real loyihada useFetch o'rniga TanStack Query (cache, retry, dedup — 12.4) — lekin tushunish uchun shu
useDebouncevauseFetch—useEffectasosli, eng amaliy custom hooklar.useDebounce— qiymatni kechiktirish: tez o'zgaradigan qiymatdan (har harf yozilganda) faqat oxirgisini olib, biroz kutib (400ms) qaytaradi — qidiruvda har harfda so'rov yubormaslik uchun ideal. IchidasetTimeout+ cleanup (yangi qiymat kelsa eski taymerni bekor qiladi — 11.5: 2.7).useFetch— ma'lumot olishning qayta ishlatiladigan hook'i:data/loading/errorstate'larini boshqaradi,urlo'zgarganda qayta yuklaydi, race condition'dan himoyalanadi (ignorebayrog'i — eski javob yangisini bosmasin — 11.5: 2.7), va obyekt qaytaradi. Bu ikkalasi — komponentdagi takrorlanuvchi mantiqni (debounce + fetch — har sahifada qayta yoziladigan) bir marta hookga ajratadi. Muhim eslatma: real loyihadauseFetcho'rniga TanStack Query 12.4-bob ishlatiladi — u cache, qayta urinish (retry), so'rovlarni birlashtirish (dedup), fonda yangilash kabilarni avtomatik qiladi; lekinuseFetchni qo'lda yozish —useEffect+fetch mexanizmini chuqur tushunish uchun bebaho.
2.12. DOM/event hooklari — useWindowSize, useOnClickOutside, useMediaQuery
useWindowSize — oyna o'lchamini kuzatish (resize listener + cleanup):
function useWindowSize() {
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
useEffect(() => {
const onResize = () => setSize({ width: window.innerWidth, height: window.innerHeight });
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize); // cleanup (11.5: 2.7)
}, []);
return size;
}
useOnClickOutside — element TASHQARISIGA bosishni aniqlash (modal/dropdown yopish):
function useOnClickOutside(ref, handler) {
useEffect(() => {
const listener = (e) => {
if (!ref.current || ref.current.contains(e.target)) return; // ichida — e'tibormaY
handler(); // tashqarida — handler (yop)
};
document.addEventListener("mousedown", listener);
return () => document.removeEventListener("mousedown", listener);
}, [ref, handler]);
}
useMediaQuery — responsive shart (CSS media query JS'da):
function useMediaQuery(query) {
const [matches, setMatches] = useState(() => window.matchMedia(query).matches);
useEffect(() => {
const mql = window.matchMedia(query);
const onChange = () => setMatches(mql.matches);
mql.addEventListener("change", onChange);
return () => mql.removeEventListener("change", onChange);
}, [query]);
return matches;
}
// const isMobile = useMediaQuery("(max-width: 768px)");
Bu hooklar — DOM/brauzer API'ni 11.1-bob React state'ga "ulaydi" (effekt+listener+cleanup naqshi)DOM/event hooklari — brauzer API'sini React state'ga "ulaydigan" amaliy hooklar (hammasi
useEffect+ listener + cleanup naqshiga asoslanadi — 11.5: 2.7).useWindowSize— oyna o'lchamini kuzatadi (resizelistener + cleanup), responsive layout uchun.useOnClickOutside— berilgan element tashqarisiga bosishni aniqlaydi (ref.current.contains(e.target)— bosilgan joy element ichidami?) — modal/dropdown'ni tashqariga bosganda yopish uchun klassik.useMediaQuery— CSS media query'ni JavaScript'da o'qiydi (window.matchMedia), responsive shart uchun (const isMobile = useMediaQuery("(max-width: 768px)")) — JSX'da shartli render qilish imkonini beradi. Bu hooklar 11.1-bobdagi DOM/event bilimini (querySelector, addEventListener, contains) React'ning deklarativ dunyosiga ulaydi: imperativ brauzer API'sini bir marta hookga o'rab, butun ilovada deklarativ tarzda ishlatasiz. Hammasida cleanup bor (listener'ni o'chirish — memory leak'dan saqlanish) — bu DOM hooklarining majburiy qoidasi.
2.13. Qachon custom hook ajratish (vs inline)
CUSTOM HOOK AJRATISH BELGILARI (qachon ajratish kerak):
BIR XIL stateful mantiq 2+ joyda takrorlansa (DRY — eng aniq belgi)
Komponentda mantiq UI'dan KO'P bo'lib ketsa (komponent "to'lib" ketdi)
Murakkab effekt/cleanup (debounce, subscription) — alohida sinash kerak
Mantiqqa AYNIQSA YAXSHI nom bersa bo'lsa (useAuth, useCart — o'zini tushuntiradi)
AJRATMA (inline qoldir):
Bir marta ishlatiladigan oddiy mantiq (ortiqcha abstraksiya)
Faqat hisob (hook emas — oddiy funksiya yetadi, "use" kerak emas — 2.2)
"Balki kerak bo'lar" — oldindan ajratma (haqiqiy takror chiqsa ajrat)
┌────────────────────────────────────────────────────────────┐
│ Takror ko'rinsa ajrating. Bir marta inline. (premature emas)│
└────────────────────────────────────────────────────────────┘
"Rule of three": bir xil mantiq 3-marta yozilsa — endi custom hook vaqti keldi
Har hook aniq, bitta mas'uliyatli bo'lsin (9.1 SRP) — "xudo hook" yozmaQachon custom hook ajratish — bu ham balans masalasi (11.6: 2.13 kabi). Ajratish belgilari: (1) bir xil stateful mantiq 2+ joyda takrorlansa (DRY — eng aniq belgi); (2) komponentda mantiq UI'dan ko'p bo'lib ketsa (komponentni "to'ldirsa" — toza qilish uchun ajratiladi); (3) murakkab effekt/cleanup (debounce, subscription) — alohida sinash kerak bo'lsa; (4) mantiqqa ayniqsa yaxshi nom berish mumkin bo'lsa (
useAuth,useCart— o'zini tushuntiradi, niyatni bildiradi). Ajratmaslik kerak (inline qoldirish): bir marta ishlatiladigan oddiy mantiq (ortiqcha abstraksiya — kodni murakkablashtiradi); faqat hisob qiladigan narsa (bu oddiy funksiya, hook emas — 2.2); va "balki kerak bo'lar" deb oldindan ajratmaslik (haqiqiy takror chiqsa ajratiladi). Foydali qoida — "Rule of three": bir xil mantiqni uchinchi marta yozish paytida endi custom hook vaqti keladi. Va har hook aniq, bitta mas'uliyatli bo'lsin (9.1 SRP) — hamma narsani qiladigan "xudo hook" (useEverything) yozmaslik kerak; kichik, fokuslangan hooklar birlashtiriladi 2.8-bob. Avval oddiy yozib, takror sezilganda ajratiladi.
2.14. Sinash va tashqi kutubxonalar
CUSTOM HOOK SINASH (test) — komponentdan MUSTAQIL (hook — sof mantiq):
- @testing-library/react'ning renderHook() bilan 11.17-bob
- hook chaqiriladi, qaytgan qiymat va o'zgarishlar tekshiriladi (komponentsiz)
custom hook'ning afzalligi: UI'dan ajralgan mantiq izolyatsiyalangan test
TAYYOR KUTUBXONALAR (har narsani noldan yozma):
- usehooks-ts — TypeScript bilan, 50+ tayyor hook (useLocalStorage, useDebounce...)
- react-use — katta to'plam (useAsync, useToggle, useInterval...)
- @uidotdev/usehooks — zamonaviy, sifatli to'plam
- ahooks — korxona darajasidagi keng to'plam (Alibaba; useRequest kuchli)
┌────────────────────────────────────────────────────────────┐
│ Oddiy/standart hooklar tayyor kutubxonadan ol (sinalgan) │
│ Loyihaga XOS mantiq (useCart, useAuth) o'zing yoz │
└────────────────────────────────────────────────────────────┘
Tayyor hook — sinalgan, edge-case'lar hisobga olingan (SSR, cleanup) ishonchli
Lekin TUSHUNISH SHART: kutubxonani ishlatsa ham, ichida nima bo'layotganini bil (bu bob)Sinash va tashqi kutubxonalar — custom hooklarning ikki amaliy jihati. Sinash: custom hook komponentdan mustaqil sinaladi (u — UI'dan ajralgan sof mantiq) —
@testing-library/reactningrenderHook()bilan hookni chaqirib, qaytgan qiymat va o'zgarishlarni komponentsiz tekshirasiz 11.17-bob. Bu — custom hookga ajratishning yana bir afzalligi: mantiq izolyatsiyalangan holda, oson sinaladi. Tashqi kutubxonalar: ko'p standart hook allaqachon yozilgan va sinalgan — har narsani noldan yozish shart emas: usehooks-ts (TypeScript, 50+ hook), react-use (katta to'plam), @uidotdev/usehooks (zamonaviy), ahooks (korxona darajasidagi keng to'plam, ayniqsauseRequest). Amaliy qoida: oddiy/standart hooklar (useLocalStorage,useDebounce,useMediaQuery) — tayyor kutubxonadan olinadi (ular sinalgan, SSR/cleanup kabi edge-case'lar hisobga olingan — ishonchliroq); loyihaga xos mantiq (useCart,useAuth,useCheckout) — o'zingiz yozasiz. Lekin tayyor kutubxona ishlatilganda ham, ichida nima bo'layotganini tushunish shart (aynan shu bob shu uchun) — uni qora quti sifatida emas, mexanizmini bilgan holda ishlatish debug va to'g'ri qo'llash uchun zarur.
2.15. Custom hook vs oddiy util funksiya (chegara aniq)
SAVOL: mantiqni custom hook'ga qo'yaymi yoki oddiy util funksiyagami?
BELGI — ichida React STATE yoki EFFEKT bormi (useState/useEffect/useRef/useContext...)?
HA custom hook kerak ("use" prefiksi, Rules of Hooks amal qiladi)
YO'Q oddiy util funksiya (import qilib istalgan joyda ishlat — hook emas)
MISOL:
formatDate(d) sof hisob, state yo'q util funksiya (utils/date.js)
isValidEmail(s) sof tekshiruv, state yo'q util funksiya
useLocalStorage() useState + useEffect ichida custom hook
useDebounce() useState + useEffect ichida custom hook
┌────────────────────────────────────────────────────────────┐
│ State/effekt kerak emas util funksiya (oddiy, tez, tekin) │
│ State/effekt kerak custom hook (React hayot siklida) │
└────────────────────────────────────────────────────────────┘
"use" prefiksini FAQAT ichida hook bo'lsa qo'y — aks holda ESLint yolg'on ogohlantiradiCustom hook vs oddiy util funksiya — amaliy chegara, ko'p yangi boshlovchi shu yerda adashadi (hamma narsani hook qilib yuboradi). Yagona mezon: funksiya ichida React state yoki effekt kerakmi? Agar mantiq shunchaki kirishdan chiqishni hisoblasa (sanani formatlash, email tekshirish, massivni saralash) — bu sof funksiya, unda hech qanday hook yo'q, demak u oddiy util funksiya (
utils/papkasida,useprefiksisiz, istalgan joyda — komponent, boshqa funksiya, hatto server'da — chaqiriladi). Agar mantiquseState/useEffect/useRef/useContextkabi hookni ishlatsa (holatni saqlash, tashqi tizimga ulanish, hayot sikliga bog'lanish) — bu custom hook (useprefiksi bilan, faqat komponent/hook ichida). Muhim:useprefiksini faqat ichida hook bo'lsa qo'ying —useFormatDatekabi nom (ichida hook yo'q) ESLint'ni chalg'itadi va Rules of Hooks tekshiruvini keraksiz qo'llaydi. Qoida sodda: state/effekt yo'q util; bor hook.
2.16. Custom hook va TypeScript (generic, tuple as const)
TYPESCRIPT'DA CUSTOM HOOK — ikki asosiy naqsh (14-QISM'da chuqur):
1) GENERIC <T> — hook turli tip bilan ishlaydi va tipni SAQLAYDI:
function useLocalStorage<T>(key: string, initial: T): [T, (v: T) => void] { ... }
const [n] = useLocalStorage("count", 0); // n: number (avtomatik chiqariladi)
2) MASSIV QAYTARSA — "as const" (aks holda tip keng/noaniq bo'ladi):
return [value, toggle]; // tip: (boolean | (() => void))[] — chalkash
return [value, toggle] as const; // tip: [boolean, () => void] — aniq tuple
┌────────────────────────────────────────────────────────────┐
│ Generic hook qayta ishlatiluvchi VA tip-xavfsiz │
│ as const massiv qaytarish uchun aniq tuple tipi (destruktr)│
└────────────────────────────────────────────────────────────┘
Obyekt qaytarsa "as const" shart emas (nomlar tipni saqlaydi) — massivda esa SHARTCustom hook va TypeScript — TypeScript loyihada (14-QISM) custom hook tip-xavfsiz bo'lishi uchun ikki naqsh bilinadi. (1) Generic
<T>: hookni turli tip bilan ishlatish va tipni saqlab qolish uchun —useFetch<User>(url)yozilgandadataavtomatikUsertipini oladi,useLocalStorage<Theme>esaThemeni. Generic bo'lmasa hookanyqaytaradi va tip xavfsizligi yo'qoladi. (2)as consttuple: hook massiv qaytarganda (return [value, toggle]), TypeScript uni keng tip —(boolean | (() => void))[]— deb chiqaradi, natijada destrukturdavalueham funksiya, ham boolean bo'lishi mumkin ko'rinadi (tip chalkashadi).as constqo'shilsa (return [value, toggle] as const), TypeScript uni aniq tuple —[boolean, () => void]— deb biladi va destruktur to'g'ri ishlaydi (xuddiuseStatedek). Obyekt qaytargandaas constshart emas — obyekt nomlari tiplarni o'zi saqlaydi; muammo faqat massiv (tuple)da. Bu ikki naqsh — Misol 22'da; batafsili 14-QISM'da.
3. Sintaksis — tez ma'lumotnoma
TUZILISH 2.2-bob: function useX(args) { /* useState/useEffect... */ return natija; }
QOIDA 2.3-bob: nom "use" bilan; Rules of Hooks (tepada, shartsiz — 11.5: 2.2)
QAYTARISH 2.6-bob: return [a, b] (1-2, nomlanadigan) | return { a, b, c } (3+, tanlab)
ARGUMENT 2.7-bob: useFetch(url, { enabled }) ; effektda ishlatilsa deps'ga qo'sh
KOMPOZITSIYA2.8-bob:function useBig() { const x=useSmall1(); const y=useSmall2(); ... }
ULASHISH 2.4-bob: har chaqiruv O'Z state'i; state ulashish kerak bo'lsa Context 12.1-bob
HOOK vs UTIL2.15-bob:ichida hook bor useX ; sof hisob oddiy funksiya (util)
TYPESCRIPT2.16-bob: function useX<T>(...) ; massiv qaytsa "return [...] as const"
SINASH 2.14-bob: renderHook(() => useX()) // komponentsiz 11.17-bob
KUTUBXONA: usehooks-ts, react-use, @uidotdev/usehooks, ahooks4. Batafsil kod namunalari
Misol 1 — useToggle (eng oddiy custom hook — 2.9)
import { useState, useCallback } from "react";
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue(v => !v), []); // updater + barqaror (11.6)
const setTrue = useCallback(() => setValue(true), []);
const setFalse = useCallback(() => setValue(false), []);
return { value, toggle, setTrue, setFalse }; // obyekt (3+ — 2.6)
}
// Ishlatish — modal:
function Modal() {
const { value: isOpen, toggle, setFalse } = useToggle(false);
return (
<>
<button onClick={toggle}>Modal ochish</button>
{isOpen && (
<div className="modal">
<p>Modal kontenti</p>
<button onClick={setFalse}>Yopish</button>
</div>
)}
</>
);
}Misol 2 — useCounter (metodlar bilan)
function useCounter(initial = 0, { min = -Infinity, max = Infinity } = {}) {
const [count, setCount] = useState(initial);
const increment = useCallback(() => setCount(c => Math.min(c + 1, max)), [max]);
const decrement = useCallback(() => setCount(c => Math.max(c - 1, min)), [min]);
const reset = useCallback(() => setCount(initial), [initial]);
return { count, increment, decrement, reset };
}
function Quantity() {
const { count, increment, decrement } = useCounter(1, { min: 1, max: 10 }); // 1-10 oralig'ida
return (
<div>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
);
}Misol 3 — usePrevious (useRef bilan — 2.9)
import { useRef, useEffect } from "react";
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value; // har render'dan keyin joriy'ni sakla
}, [value]);
return ref.current; // return effektdan OLDIN oldingi qiymat (11.5: 2.13)
}
function PriceTracker({ price }) {
const prevPrice = usePrevious(price);
const trend = prevPrice === undefined ? "" : price > prevPrice ? "" : price < prevPrice ? "" : "";
return <p>Narx: {price} {trend} (oldin: {prevPrice ?? "—"})</p>;
}Misol 4 — useLocalStorage (state + persistence — 2.10)
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const saved = localStorage.getItem(key);
return saved !== null ? JSON.parse(saved) : initialValue; // lazy init (11.5)
} catch {
return initialValue; // JSON xato bo'lca — boshlang'ich
}
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value)); // value o'zgapganda saqla (11.5: 2.8)
}, [key, value]);
return [value, setValue]; // useState bilan bir xil interfeys
}
// Ishlatish — saqlanadigan mavzu:
function App() {
const [theme, setTheme] = useLocalStorage("theme", "light");
return (
<div className={theme}>
<button onClick={() => setTheme(t => (t === "light" ? "dark" : "light"))}>
Mavzu: {theme}
</button>
</div>
);
}
// Sahifani yangilab ochsang ham — theme saqlangan qoladi (2.10)Misol 5 — useDebounce (qiymat kechiktirish — 2.11)
function useDebounce(value, delay = 400) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer); // yangi value kelsa — eski timerni bekor (11.5: 2.7)
}, [value, delay]);
return debounced;
}
function Search() {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 400); // 400ms to'xtagach yangilanadi
useEffect(() => {
if (debouncedQuery) console.log("Qidirilmoqda:", debouncedQuery); // bu yerda fetch
}, [debouncedQuery]); // har harfda emas — to'xtagach
return <input value={query} onChange={e => setQuery(e.target.value)} />;
}Misol 6 — useFetch (ma'lumot olish — 2.11)
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (!url) return; // url bo'lmaca — yuklama
let ignore = false; // race himoyasi (11.5: 2.7)
setLoading(true); setError(null);
fetch(url)
.then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
.then(d => { if (!ignore) setData(d); })
.catch(e => { if (!ignore) setError(e.message); })
.finally(() => { if (!ignore) setLoading(false); });
return () => { ignore = true; }; // cleanup: eski javobni rad et
}, [url]);
return { data, loading, error };
}
function UserList() {
const { data: users, loading, error } = useFetch("/api/users");
if (loading) return <Spinner />;
if (error) return <p className="error">{error}</p>;
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
// Real loyihada — TanStack Query 12.4-bob; bu hook mexanizmni tushunish uchunMisol 7 — useWindowSize (resize listener — 2.12)
function useWindowSize() {
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
useEffect(() => {
const onResize = () => setSize({ width: window.innerWidth, height: window.innerHeight });
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize); // cleanup (11.5: 2.7)
}, []);
return size;
}
function Responsive() {
const { width } = useWindowSize();
return <p>{width < 768 ? " Mobil" : " Desktop"} ({width}px)</p>;
}Misol 8 — useOnClickOutside (modal/dropdown yopish — 2.12)
function useOnClickOutside(ref, handler) {
useEffect(() => {
const listener = (e) => {
if (!ref.current || ref.current.contains(e.target)) return; // ichida bosildi — e'tibormaY
handler(); // tashqarida — handler (yop)
};
document.addEventListener("mousedown", listener);
return () => document.removeEventListener("mousedown", listener);
}, [ref, handler]);
}
function Dropdown() {
const [open, setOpen] = useState(false);
const ref = useRef(null);
useOnClickOutside(ref, () => setOpen(false)); // tashqariga bosca yopiladi
return (
<div ref={ref} className="dropdown">
<button onClick={() => setOpen(o => !o)}>Menyu</button>
{open && <ul className="menu"><li>Profil</li><li>Chiqish</li></ul>}
</div>
);
}Misol 9 — useMediaQuery (responsive shart — 2.12)
function useMediaQuery(query) {
const [matches, setMatches] = useState(() => window.matchMedia(query).matches);
useEffect(() => {
const mql = window.matchMedia(query);
const onChange = () => setMatches(mql.matches);
mql.addEventListener("change", onChange);
return () => mql.removeEventListener("change", onChange);
}, [query]);
return matches;
}
function Layout() {
const isMobile = useMediaQuery("(max-width: 768px)");
return isMobile ? <MobileNav /> : <DesktopNav />; // shartli komponent (11.4: 2.12)
}Misol 10 — useInterval (Dan Abramov naqshi — ref bilan stale closure yechimi)
function useInterval(callback, delay) {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback; // har render'da eng yangi callback'ni sakla
}, [callback]);
useEffect(() => {
if (delay === null) return; // null to'xta (pauza)
const id = setInterval(() => savedCallback.current(), delay); // ref'dan eng yangini chaqir
return () => clearInterval(id); // cleanup
}, [delay]); // faqat delay o'zgapganda qayta o'rnat
}
// Ref naqshi — stale closure'ni (11.5: 2.9) hal qiladi: callback yangilanadi,
// lekin interval qayta o'rnatilmaydi (delay o'zgapmaca). Klassik custom hook namunasi.
function Timer() {
const [count, setCount] = useState(0);
const [running, setRunning] = useState(true);
useInterval(() => setCount(c => c + 1), running ? 1000 : null); // null pauza
return <button onClick={() => setRunning(r => !r)}>{count} {running ? "" : "▶"}</button>;
}Misol 11 — Kompozitsiya: useDebouncedSearch (hook + hook — 2.8)
// Ikki kichik hookni birlashtirib (compose) yuqori darajadagi mantiq:
function useDebouncedSearch(query, delay = 400) {
const debouncedQuery = useDebounce(query, delay); // hook 1 (Misol 5)
const { data, loading, error } = useFetch( // hook 2 (Misol 6)
debouncedQuery ? `/api/search?q=${encodeURIComponent(debouncedQuery)}` : null
);
return { results: data ?? [], loading, error };
}
function SearchPage() {
const [query, setQuery] = useState("");
const { results, loading } = useDebouncedSearch(query); // bitta toza hook
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} placeholder="Qidirish..." />
{loading && <small>Qidirilmoqda...</small>}
<ul>{results.map(r => <li key={r.id}>{r.title}</li>)}</ul>
</div>
);
}
// useDebounce + useFetch = useDebouncedSearch (mantiq kompozitsiyasi — 2.8)Misol 12 — useForm (kichik forma hook'i)
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const handleChange = useCallback((e) => {
const { name, value } = e.target;
setValues(prev => ({ ...prev, [name]: value })); // dinamik maydon (11.4: 2.8)
}, []);
const reset = useCallback(() => { setValues(initialValues); setErrors({}); }, [initialValues]);
return { values, errors, setErrors, handleChange, reset };
}
function ContactForm() {
const { values, handleChange, reset } = useForm({ name: "", email: "" });
return (
<form onSubmit={(e) => { e.preventDefault(); console.log(values); reset(); }}>
<input name="name" value={values.name} onChange={handleChange} />
<input name="email" value={values.email} onChange={handleChange} />
<button>Yuborish</button>
</form>
);
}
// Bu — soddalashtirilgan forma hook'i; real loyihada React Hook Form 11.10-bob ancha kuchliMisol 13 — State ulashilmasligini ko'rsatish (2.4)
// Bitta hook — ikki komponentda — IKKI ALOHIDA state (ulashilmaydi!)
function Counter({ label }) {
const { count, increment } = useCounter(0); // har Counter o'z count'i (Misol 2)
return <button onClick={increment}>{label}: {count}</button>;
}
function App() {
return (
<>
<Counter label="A" /> {/* count_A — mustaqil */}
<Counter label="B" /> {/* count_B — mustaqil (A'ga aloqasi yo'q) */}
</>
);
}
// A'ni bosca faqat A oshadi (B o'zgapmaydi) — hook MANTIQNI ulashadi, STATE'ni emas (2.4)
// Agar A va B BIR XIL count ko'rsin desang Context 12.1-bob yoki Redux (12.2)Misol 14 — useAuth (loyihaga xos hook — Context bilan — 2.4, 2.8)
// Context + custom hook = global auth (state ULASHILadi — chunki Context — 2.4)
const AuthContext = createContext();
export function AuthProvider({ children }) {
const [user, setUser] = useLocalStorage("user", null); // saqlanadigan (Misol 4)
const login = useCallback(async (creds) => {
const data = await api.login(creds);
setUser(data.user);
}, [setUser]);
const logout = useCallback(() => setUser(null), [setUser]);
const value = useMemo(() => ({ user, login, logout }), [user, login, logout]); // barqaror (11.6: 2.9)
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
// Custom hook — Context'ni qulay o'rab beradi + xato tekshiruvi:
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useAuth AuthProvider ichida ishlatilishi kerak"); // foydali xato
return ctx;
}
// Istalgan joyda:
function Navbar() {
const { user, logout } = useAuth();
return user ? <button onClick={logout}>Chiqish</button> : <a href="/login">Kirish</a>;
}
// useLocalStorage + Context + useMemo birlashmasi — real auth naqshi (2.8 kompozitsiya)Misol 15 — useEventListener (universal listener hook — 2.12)
useWindowSize, useOnClickOutside va boshqa DOM hooklar ichida bir xil naqsh takrorlanadi: addEventListener cleanup'da removeEventListener. useEventListener — shu naqshni bitta joyga jamlaydigan "poydevor" hook (boshqa hooklarni ishlab chiqarish uchun ishlatiladi). Handler'ni useRef'da saqlash — useInterval'dagi (Misol 10) stale closure yechimi bilan bir xil: effekt handler o'zgarganda qayta o'rnatilmaydi, lekin har doim eng yangi handler'ni chaqiradi.
import { useRef, useEffect } from "react";
function useEventListener(eventName, handler, element = window) {
const savedHandler = useRef(handler);
useEffect(() => {
savedHandler.current = handler; // eng yangi handler'ni ref'da sakla (stale closure yo'q — Misol 10)
}, [handler]);
useEffect(() => {
const target = element?.current ?? element; // ref bo'lsa .current, aks holda o'zi (window/document)
if (!target?.addEventListener) return;
const listener = (e) => savedHandler.current(e); // ref orqali chaqir — doim yangi
target.addEventListener(eventName, listener);
return () => target.removeEventListener(eventName, listener); // cleanup (11.5: 2.7)
}, [eventName, element]); // handler deps'da EMAS — ref hal qildi
}
function ScrollTracker() {
const [y, setY] = useState(0);
useEventListener("scroll", () => setY(window.scrollY)); // handler har render yangi — muammo yo'q
return <p>Scroll: {Math.round(y)}px</p>;
}
// Poydevor hook: useWindowSize/useOnClickOutside'ni endi shu ustiga qurish mumkin (2.8)Misol 16 — useThrottle (chastotani cheklash — debounce'ning "qardoshi")
useDebounce (Misol 5) tez o'zgarishning oxirgisini kutadi; useThrottle esa qiymatni muntazam oraliqda (masalan har 200ms'da bir marta) yangilaydi — to'xtashni kutmaydi. Scroll/resize/mousemove kabi uzluksiz hodisalar uchun ideal (debounce ular tinim olmasa hech qachon ishlamaydi). Farqni bilish muhim: debounce — "jim bo'lguncha kut", throttle — "belgilangan tezlikda o'tkaz".
function useThrottle(value, interval = 200) {
const [throttled, setThrottled] = useState(value);
const lastRun = useRef(Date.now());
useEffect(() => {
const sinceLast = Date.now() - lastRun.current;
if (sinceLast >= interval) {
lastRun.current = Date.now();
setThrottled(value); // interval o'tgan — darrov yangila
} else {
const timer = setTimeout(() => { // interval to'lguncha kut, keyin yangila
lastRun.current = Date.now();
setThrottled(value);
}, interval - sinceLast);
return () => clearTimeout(timer); // cleanup (11.5: 2.7)
}
}, [value, interval]);
return throttled;
}
// const throttledScroll = useThrottle(scrollY, 200); // har 200ms'da eng ko'pi bir yangilanish
// Debounce (oxirini kut) vs Throttle (muntazam o'tkaz) — ikkalasi ham "chastotani cheklash", turli holatgaMisol 17 — useCopyToClipboard (Clipboard API + holat)
function useCopyToClipboard() {
const [copied, setCopied] = useState(false);
const copy = useCallback(async (text) => {
if (!navigator?.clipboard) { // eski brauzer — qo'llab-quvvatlanmaydi
setCopied(false);
return false;
}
try {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000); // 2s'dan keyin "nusxalandi" holatini o'chir
return true;
} catch {
setCopied(false);
return false;
}
}, []);
return { copied, copy }; // 2+ qiymat obyekt (2.6)
}
function ShareButton({ link }) {
const { copied, copy } = useCopyToClipboard();
return (
<button onClick={() => copy(link)}>
{copied ? " Nusxalandi" : " Havolani nusxalash"}
</button>
);
}
// Async brauzer API'sini (Clipboard) + vaqtinchalik holatni (copied) qulay hookga o'raydiMisol 18 — useIntersectionObserver (lazy load / infinite scroll)
IntersectionObserver — element ekranga (viewport'ga) kirdi-chiqdimi, buni samarali kuzatadigan brauzer API'si (scroll'ni qo'lda hisoblashdan tez). Ikki asosiy qo'llanish: lazy load (rasm ko'rinmaguncha yuklamaslik) va infinite scroll (ro'yxat oxiriga yetganda keyingi sahifani yuklash).
function useIntersectionObserver(options) {
const [entry, setEntry] = useState(null);
const ref = useRef(null);
useEffect(() => {
const node = ref.current;
if (!node || !("IntersectionObserver" in window)) return;
const observer = new IntersectionObserver(([e]) => setEntry(e), options);
observer.observe(node);
return () => observer.disconnect(); // cleanup — observer'ni uz (11.5: 2.7)
}, [options]); // options barqaror bo'lsin (useMemo — 2.7)
return [ref, entry]; // [element'ga ulanadigan ref, kuzatuv natijasi]
}
// Infinite scroll: "sentinel" element ekranga kirsa — keyingi sahifa
function Feed({ loadMore }) {
const [ref, entry] = useIntersectionObserver({ threshold: 1 });
useEffect(() => {
if (entry?.isIntersecting) loadMore(); // oxirdagi element ko'rindi yukla
}, [entry?.isIntersecting, loadMore]);
return <div ref={ref} className="sentinel">Yuklanmoqda...</div>;
}
// options obyekti chaqiruvchida useMemo bilan barqaror bo'lsin — aks holda effekt har render qayta ishlaydi (2.7, Xato 4)Misol 19 — useAsync (ixtiyoriy async funksiyani boshqarish)
useFetch (Misol 6) faqat fetchga bog'langan. useAsync esa istalgan async funksiyani (login, fayl yuklash, hisob-kitob) idle/pending/success/error holatlari bilan boshqaradi — umumiylashtirilgan naqsh.
function useAsync(asyncFn) {
const [state, setState] = useState({ status: "idle", data: null, error: null });
const run = useCallback(async (...args) => {
setState({ status: "pending", data: null, error: null });
try {
const data = await asyncFn(...args);
setState({ status: "success", data, error: null });
return data;
} catch (error) {
setState({ status: "error", data: null, error });
throw error;
}
}, [asyncFn]);
return { ...state, run, isLoading: state.status === "pending" };
}
function SaveButton({ save }) {
const { run, isLoading, status } = useAsync(save);
return (
<button onClick={() => run()} disabled={isLoading}>
{isLoading ? "Saqlanmoqda..." : status === "success" ? "Saqlandi " : "Saqlash"}
</button>
);
}
// useFetch — "fetch"ga xos; useAsync — istalgan Promise'ga umumiy (login, upload, hisob)Misol 20 — useDisclosure (ochiq/yopiq holat — modal/drawer uchun)
useToggle'ning "aniq niyatli" versiyasi: open/close/toggle — nom orqali o'zini tushuntiradi (modal, drawer, accordion boshqaruvi uchun standart naqsh; Chakra UI, Mantine kabi kutubxonalarda ham shu nom bor).
function useDisclosure(initial = false) {
const [isOpen, setIsOpen] = useState(initial);
const open = useCallback(() => setIsOpen(true), []);
const close = useCallback(() => setIsOpen(false), []);
const toggle = useCallback(() => setIsOpen((v) => !v), []);
return { isOpen, open, close, toggle };
}
function Page() {
const modal = useDisclosure(); // bitta obyektda hammasi
return (
<>
<button onClick={modal.open}>Ochish</button>
{modal.isOpen && (
<div className="modal">
<p>Modal</p>
<button onClick={modal.close}>Yopish</button>
</div>
)}
</>
);
}
// Bitta obyekt qaytarish ko'p modal boshqarilsa qulay: const create = useDisclosure(); const edit = useDisclosure();Misol 21 — useFetch (kengaytirilgan: AbortController + refetch)
Misol 6'dagi useFetch — asos. Real ilovada yana ikki narsa kerak: (1) AbortController — komponent olib tashlanganda yoki url o'zgarganda haqiqiy tarmoq so'rovini bekor qilish (ignore bayrog'i faqat natijani e'tiborsiz qoldiradi, so'rov esa davom etadi — AbortController so'rovning o'zini to'xtatadi); (2) refetch — foydalanuvchi "qayta urinish" bosganda so'rovni qo'lda takrorlash.
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [reloadKey, setReloadKey] = useState(0); // refetch uchun "trigger"
const refetch = useCallback(() => setReloadKey((k) => k + 1), []); // barqaror (2.6)
useEffect(() => {
if (!url) return;
const controller = new AbortController(); // so'rovni bekor qilish uchun
setLoading(true);
setError(null);
fetch(url, { signal: controller.signal })
.then((r) => {
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
})
.then((d) => setData(d))
.catch((e) => {
if (e.name !== "AbortError") setError(e.message); // bekor qilish — xato emas, e'tiborsiz qoldir
})
.finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort(); // cleanup: so'rovni HAQIQATAN to'xtat (11.5: 2.7)
}, [url, reloadKey]); // reloadKey o'zgarsa — qayta yuklaydi
return { data, loading, error, refetch }; // refetch ham qaytariladi
}
function Products() {
const { data, loading, error, refetch } = useFetch("/api/products");
if (loading) return <Spinner />;
if (error) return <button onClick={refetch}>Xato — qayta urinish</button>; // refetch ishlatilishi
return <ul>{data.map((p) => <li key={p.id}>{p.name}</li>)}</ul>;
}
// AbortController — real so'rovni to'xtatadi (ignore-bayroq faqat natijani tashlaydi); refetch — qo'lda qayta yuklashMisol 22 — TypeScript bilan custom hook (generic + tuple as const — 14-bob)
TypeScript'da custom hook yozganda ikki muhim naqsh bor (14-QISM'da chuqur): (1) generic <T> — hook turli tiplar bilan ishlaydi va tipni saqlaydi (useFetch<User> data: User); (2) massiv qaytarganda as const — TypeScript uni aniq tuple deb biladi ([boolean, () => void]), aks holda (boolean | (() => void))[] deb keng, noaniq tip chiqaradi va destrukturda tiplar chalkashadi.
import { useState, useCallback, useEffect } from "react";
// Generic — qiymat tipini saqlaydi:
function useLocalStorage<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() => {
const saved = localStorage.getItem(key);
return saved !== null ? (JSON.parse(saved) as T) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue] as const; // tuple: [T, Dispatch<SetStateAction<T>>]
}
// Ishlatish — tip avtomatik chiqariladi (inference):
const [count, setCount] = useLocalStorage("count", 0); // count: number
const [user, setUser] = useLocalStorage<User>("user", null!); // user: User
// Toggle — aniq tuple tipi:
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue((v) => !v), []);
return [value, toggle] as const; // [boolean, () => void] — aniq
}
// `as const` bo'lmasa — return tip (boolean | (() => void))[] bo'lib, destrukturda tiplar aralashadi (14-bob)5. To'g'ri va noto'g'ri holatlar
1) Nomlash
function getToggle() { useState(...) } ("use" yo'q — ESLint hook deb ko'rmaydi — 2.3)
function useToggle() { useState(...) } ("use" prefiksi — majburiy)2) Hook joylashuvi
if (x) { const v = useFetch(url); } (shart ichida — Rules buzildi — 2.3)
const v = useFetch(x ? url : null); if (x) {...} (tepada; shartni argument bilan)3) State ulashish kutilmasi
ikki komponentda useCart() — bir xil savat ko'rsin deb kutish (alohida state — 2.4)
ulashiladigan state Context/Redux; custom hook faqat mantiq (2.4)4) Oddiy funksiyani hook qilish
function useFormatDate(d) { return d.toLocaleString(); } (hook yo'q ichida — 2.2)
function formatDate(d) {...} (oddiy funksiya — "use" kerak emas)5) Argument deps
function useFetch(url) { useEffect(() => fetch(url), []) } (url deps'da yo'q — 2.7)
}, [url]) (argument effektda deps'ga qo'sh — 11.5: 2.6)6) Oldindan ajratish
har kichik mantiqni darrov hookga ajratish (ortiqcha abstraksiya — 2.13)
takror (2+) yoki murakkablik chiqsa ajrat (Rule of three)6. Keng tarqalgan xatolar va yechimlari
Xato 1 — React Hook "useState" is called in function "getX" which is neither a component nor a custom hook
Sababi: ichida hook bor funksiya use bilan boshlanmagan 2.3-bob. Yechimi: funksiyani use* deb nomla (getX useX).
Xato 2 — Custom hook'dagi state ikki komponentda "ulashilmayapti"
Sababi: custom hook state'ni emas, mantiqni ulashadi — har chaqiruv alohida 2.4-bob. Yechimi: ulashiladigan state uchun Context 12.1-bob yoki Redux/Zustand (12.2, 12.5).
Xato 3 — Rendered more hooks than during the previous render
Sababi: custom hookni shart/tsikl ichida chaqirding (2.3 — Rules of Hooks buzildi). Yechimi: hookni tepada, shartsiz chaqir; shartni hook argumentiga yoki ichiga ko'chir.
Xato 4 — useFetch/useEffect cheksiz qayta ishlaydi
Sababi: argument (url/options) har render yangi havola (obyekt — 11.6: 2.9), yoki deps noto'g'ri. Yechimi: primitiv argument; obyekt argumentni useMemo bilan barqaror ush 2.7-bob.
Xato 5 — Stale closure (interval/listener eski qiymat ko'radi)
Sababi: effekt [] bilan, ichida eski state/callback (11.5: 2.9). Yechimi: updater funksiya, yoki useRef naqshi (Misol 10 — useInterval).
Xato 6 — localStorage hook SSR'da (Next.js) xato beradi (localStorage is not defined)
Sababi: server'da window/localStorage yo'q (13-QISM). Yechimi: typeof window !== "undefined" tekshir, yoki tayyor SSR-xavfsiz hook (usehooks-ts — 2.14).
Xato 7 — useAuth must be used within AuthProvider
Sababi: Context hook'i Provider tashqarisida ishlatildi (2.4, Misol 14). Yechimi: ildizda <AuthProvider> bilan o'ra; hook'da aniq xato tashla (Misol 14).
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Barcha hooklar (11.5, 11.6): custom hook ularni birlashtiradi (useState/useEffect/useRef/useReducer).
- Komponent kompozitsiyasi (11.2: 2.9): custom hook — mantiq kompozitsiyasi (UI emas).
- Context 12.1-bob: state ulashish — custom hook bilan birga (useAuth — Misol 14).
- TanStack Query 12.4-bob: useFetch'ning professional, cache'li muqobili.
- React Hook Form 11.10-bob: useForm'ning kuchli versiyasi.
- Testing 11.17-bob:
renderHookbilan custom hook sinash (komponentsiz, izolyatsiyalangan). - TypeScript (14-QISM): generic
<T>vaas consttuple bilan tip-xavfsiz custom hook (2.16, Misol 22). - Performance 11.11-bob: hook ichida useMemo/useCallback (barqaror qaytarish).
- Next.js (13): SSR-xavfsiz hooklar (window tekshiruvi — Xato 6).
8. Eng yaxshi amaliyotlar (best practices)
useprefiksi majburiy (ESLint hookni tanisin — 2.3, Xato 1).- Bir hook — bitta mas'uliyat (SRP; "xudo hook" yozma — 2.13, 9.1).
- Rule of three (takrorni sezganda ajrat; oldindan emas — 2.13).
- Logika ulashiladi, state emas (ulashish Context/Redux — 2.4).
- Qaytarishda konvensiya (1-2 massiv; 3+ obyekt — 2.6).
- Argumentlar deps'da (
exhaustive-deps— effekt to'g'riligi — 2.7, 11.5: 2.6). - Cleanup har band qiluvchi effektda (listener/timer/subscription — 2.12, 11.5: 2.7).
- Barqaror qaytarish (funksiyalarni
useCallback, obyektlarniuseMemo— chaqiruvchi effekt deps uchun — 11.6). - Kichik hooklarni birlashtir (composition > katta monolit hook — 2.8).
- Standart hooklarni kutubxonadan ol (usehooks-ts; loyihaga xos — o'zingiz yoz — 2.14).
9. Amaliy loyiha: "O'z Hooklar Kutubxonang (Hooks Library)"
O'zingning qayta ishlatiladigan custom hooklar to'plamini yarat va ularni real ilovada ishlat.
Maqsad
8-10 ta amaliy custom hook yoz (alohida fayllarda), ularni hujjatla, va kichik ilovada (masalan blog yoki do'kon) birga ishlat.
Talablar (requirements)
useToggle— boolean almashtirish (Misol 1).useLocalStorage— saqlanadigan state, lazy init + persist (Misol 4).useDebounce— qiymat kechiktirish, cleanup bilan (Misol 5).useFetch— ma'lumot olish, loading/error/data + race himoyasi (Misol 6).useWindowSizeyokiuseMediaQuery— responsive (Misol 7, 9).useOnClickOutside— modal/dropdown yopish (Misol 8).useInterval— ref naqshi bilan, stale closure'siz (Misol 10).- Kompozitsiya: kamida bitta hook boshqa hook(lar)ni birlashtirsin (
useDebouncedSearch— Misol 11, 2.8). - To'g'ri qaytarish: 1-2 qiymat massiv, 3+ obyekt 2.6-bob; funksiyalar
useCallbackbilan barqaror. - Real ishlatish: bu hooklarni kichik ilovada ishlat (qidiruv + saqlanadigan sozlama + responsive + modal).
Maslahatlar (hint)
- Har hookni alohida faylda (
hooks/useToggle.js) —hooks/papkasi (11.3: 2.9). - Cleanup'ni unutma (listener/timer — Misol 5, 7, 8, 10).
useprefiksisiz nomlama (ESLint ogohlantiradi — 2.3).- State ulashish kerak bo'lsa (savat) — custom hook emas, Context (Misol 14, 2.4).
- Argument effektda ishlatilsa — deps'ga qo'sh (Misol 6, 2.7).
useIntervalda ref naqshini ishlat (stale closure'dan saqlanish — Misol 10).- Tugagach: o'z hooklaringni usehooks-ts bilan solishtir — qanday qilingan 2.14-bob.
"Tayyor" mezonlari (acceptance criteria)
- 8-10 custom hook, har biri alohida faylda,
useprefiksi bilan. - Har hook bitta aniq mas'uliyatli (SRP).
- Cleanup kerakli hooklarda mavjud (memory leak yo'q).
- Kamida bitta kompozitsiya (hook ichida hook).
- To'g'ri qaytarish konvensiyasi (massiv/obyekt).
- Qaytarilgan funksiyalar barqaror (useCallback).
- Hooklar real ilovada ishlatilgan.
- State ulashish kerak joyda Context (custom hook emas) ishlatilgan.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda custom hooklar — mantiqni qayta ishlatishning React usulini chuqur o'rgandik:
- Mantiq ulashish muammosi (HOC/render props custom hook — 2.1); custom hook nima (
use*funksiya — 2.2); qoidalar (useprefiksi, Rules of Hooks — 2.3). - Eng muhim — logikani ulashadi, state'ni emas (har chaqiruv izolyatsiyalangan — 2.4); ajratish jarayoni 2.5-bob; qaytarish (massiv/obyekt — 2.6); parametrlash 2.7-bob; kompozitsiya 2.8-bob.
- Amaliy hooklar:
useToggle/useCounter/usePrevious(2.9, Misol 1-3),useLocalStorage(2.10, Misol 4),useFetch/useDebounce/useThrottle(2.11, Misol 5-6, 16, 21 — AbortController+refetch bilan), DOM hooklaruseWindowSize/useOnClickOutside/useMediaQuery/useEventListener/useIntersectionObserver(2.12, Misol 7-9, 15, 18), vauseCopyToClipboard/useAsync/useDisclosure/useForm/useAuth(Misol 17, 19-20, 12, 14); hook vs util funksiya 2.15-bob; TypeScript bilan (2.16, Misol 22); qachon ajratish 2.13-bob; sinash va kutubxonalar 2.14-bob.
Endi siz mantiqni toza, qayta ishlatiladigan hooklarga ajratib, butun loyiha bo'ylab DRY va modulli kod yoza olasiz. Bu — siz 11.5-11.6'da o'rgangan barcha hooklarni amalda birlashtiradigan, eng kuchli React ko'nikmalaridan biri.
Keyingi bob — 11.8-bob: React.lazy, Suspense va code splitting. Ilovamiz o'sgani sayin, butun kodni bir vaqtda yuklash sekinlashadi. Code splitting — kodni bo'laklarga bo'lib, faqat kerak bo'lganda yuklash. React.lazy komponentni "dangasa" (kerak bo'lganda) yuklaydi, Suspense esa yuklanayotgan paytda zaxira UI (spinner) ko'rsatadi. Bu — katta ilovalarning yuklanish tezligini (performance) keskin oshiradigan muhim texnika (11.11, 13.10 bilan bog'liq).
Foydalanilgan rasmiy/ishonchli manbalar
- React rasmiy hujjati (react.dev):
- "Reusing Logic with Custom Hooks" — custom hook nima,
useprefiksi, logika ulashish (state emas), ajratish jarayoni - "Removing Effect Dependencies", "Lifecycle of Reactive Effects", "You Might Not Need an Effect" — effekt asosli hooklar (useFetch, useDebounce) va deps to'g'riligi
- "Rules of Hooks" — custom hook ichida va tashqarisida hook chaqirish qoidalari
- "Reusing Logic with Custom Hooks" — custom hook nima,
- Dan Abramov — "Making setInterval Declarative with React Hooks" (
useIntervalref naqshi, stale closure yechimi — Misol 10) - Tayyor custom hook to'plamlari (2026):
- usehooks-ts (usehooks-ts.com) — TypeScript, SSR-xavfsiz,
storageevent sinxronizatsiyasi bilan - @uidotdev/usehooks, react-use, ahooks (Alibaba —
useRequest)
- usehooks-ts (usehooks-ts.com) — TypeScript, SSR-xavfsiz,
- TanStack Query (tanstack.com/query) —
useFetchning professional muqobili (cache, retry, dedup — 12.4) - @testing-library/react —
renderHookbilan custom hook sinash (11.17) - MDN Web Docs:
localStorage,Storage: storage event(tablararo sinxronizatsiya)matchMedia(useMediaQuery),AbortController/AbortSignal(useFetch bekor qilish)IntersectionObserver(useIntersectionObserver — lazy load/infinite scroll)Clipboard API: writeText(useCopyToClipboard),Node.contains(useOnClickOutside)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!