Własne hooki: wydzielanie wielokrotnego użytku logiki
Proszę przenosić logikę wykorzystującą stan do własnych funkcji use*, aby współdzielić zachowanie między komponentami bez powielania kodu lub podnoszenia stanu.
Własne hooki: wydzielanie wielokrotnego użytku logiki to bezpłatna lekcja Frontend Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Frontend Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Frontend Academy zawiera 4 lekcji w sumie.
Czym są niestandardowe hooki
Niestandardowy hook to funkcja JavaScript, której nazwa zaczyna się od use i która wewnętrznie wywołuje inne hooki. Niestandardowe hooki pozwalają wyodrębniać logikę zależną od stanu, dzięki czemu wiele komponentów może z niej korzystać bez kopiowania kodu.
Wyodrębnianie logiki do niestandardowego hooka
Należy zidentyfikować logikę powielaną w komponentach (pobieranie danych + ładowanie + błąd, rozmiar okna, localStorage) i przenieść ją do niestandardowego hooka.
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handler = () => setWidth(window.innerWidth);
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);
return width;
}
// Usage:
const width = useWindowWidth();
const isMobile = width < 768;useFetch — hook do pobierania danych
Wielokrotnego użytku hook do pobierania danych, którego dowolny komponent może użyć do załadowania danych z adresu URL.
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController();
async function load() {
try {
const res = await fetch(url, { signal: controller.signal });
if (!res.ok) throw new Error(`${res.status}`);
setData(await res.json());
} catch (err) {
if ((err as DOMException).name !== 'AbortError')
setError((err as Error).message);
} finally { setLoading(false); }
}
load();
return () => controller.abort();
}, [url]);
return { data, loading, error };
}Hook useLocalStorage
Niestandardowy hook synchronizujący wartość z localStorage i utrzymujący synchronizację między kartami za pomocą zdarzenia storage.
function useLocalStorage<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
const set = useCallback((newValue: T) => {
setValue(newValue);
localStorage.setItem(key, JSON.stringify(newValue));
}, [key]);
return [value, set] as const;
}Niestandardowe hooki współdzielą logikę, nie stan
Każdy komponent wywołujący niestandardowy hook otrzymuje własny, niezależny stan. Hooki nie są singletonami — są wywołaniami funkcji tworzącymi odizolowane instancje stanu.
Konwencje nazewnictwa
Nazwy niestandardowych hooków muszą zaczynać się od use. Jest to egzekwowane przez reguły lintowania react-hooks w ESLint, które zapewniają, że hooki są wywoływane na najwyższym poziomie i we właściwej kolejności.
Zwracanie wielu wartości
Niestandardowe hooki zazwyczaj zwracają obiekt (z nazwanymi wartościami) lub krotkę (tak jak useState). Krotki pozwalają na destrukturyzację z własnymi nazwami, a obiekty ułatwiają zrozumienie kodu.
// Tuple (like useState):
const [value, setValue] = useLocalStorage('key', 0);
// Object (more descriptive):
const { data, loading, error, refetch } = useFetch('/api/users');Hook useDebounce
Należy opóźniać aktualizację wartości: aktualizować ją dopiero po upływie określonego czasu od zakończenia pisania przez użytkownika.
function useDebounce<T>(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
// Usage:
const debouncedSearch = useDebounce(searchTerm, 300);
useEffect(() => { search(debouncedSearch); }, [debouncedSearch]);Hook useMediaQuery
Opakowuje window.matchMedia w hook, aby reaktywnie odczytywać wyniki zapytań medialnych CSS.
function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(() => window.matchMedia(query).matches);
useEffect(() => {
const mq = window.matchMedia(query);
const handler = (e: MediaQueryListEvent) => setMatches(e.matches);
mq.addEventListener('change', handler);
return () => mq.removeEventListener('change', handler);
}, [query]);
return matches;
}Testowanie niestandardowych hooków
Należy użyć narzędzia renderHook() z @testing-library/react, aby testować niestandardowe hooki w izolacji, bez opakowywania ich w komponencie.
import { renderHook, act } from '@testing-library/react';
test('useCounter increments', () => {
const { result } = renderHook(() => useCounter(0));
expect(result.current.count).toBe(0);
act(() => result.current.increment());
expect(result.current.count).toBe(1);
});Opublikowane biblioteki hooków
Społeczność udostępnia biblioteki zawierające setki przetestowanych hooków: @uidotdev/usehooks, react-use, usehooks-ts. Przed napisaniem niestandardowego hooka należy sprawdzić, czy istnieje już dobrze przetestowana wersja.
Szybki test
Co dzieje się ze stanem wewnątrz niestandardowego hooka, gdy zostanie on wywołany przez dwa różne komponenty?
Podsumowanie: niestandardowe hooki
Niestandardowe hooki wyodrębniają logikę zależną od stanu, aby można było jej używać ponownie. Ich nazwy należy tworzyć z prefiksem use. Każdy komponent wywołujący hook otrzymuje niezależny stan. Hooki zwracają obiekty lub krotki. Typowe wzorce to useFetch, useLocalStorage, useDebounce i useMediaQuery. Do testowania należy używać renderHook(). Przed napisaniem hooka od podstaw warto sprawdzić biblioteki społeczności.
Często zadawane pytania
Czy lekcja „Własne hooki: wydzielanie wielokrotnego użytku logiki” jest bezpłatna?
Tak — pełny tekst „Własne hooki: wydzielanie wielokrotnego użytku logiki” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Frontend Academy, przejdź na CoddyKit PRO. Kurs Frontend Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Własne hooki: wydzielanie wielokrotnego użytku logiki”?
Proszę przenosić logikę wykorzystującą stan do własnych funkcji use*, aby współdzielić zachowanie między komponentami bez powielania kodu lub podnoszenia stanu. Ćwiczysz Frontend Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Frontend Academy?
Nie wymagamy żadnego doświadczenia. Frontend Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Własne hooki: wydzielanie wielokrotnego użytku logiki”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Frontend Academy?
Tak. Każda lekcja Frontend Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- useContext do stanu globalnego
- useReducer do złożonego stanu
- useMemo i useCallback na potrzeby wydajności
- Własne hooki: wydzielanie wielokrotnego użytku logiki