Odkryj moc logiki wielokrotnego użytku w aplikacjach React dzięki własnym hookom. Naucz się, jak je tworzyć, aby kod był czystszy i łatwiejszy w utrzymaniu.
Własne Hooki: Wzorce Logiki Wielokrotnego Użytku w React
Hooki React zrewolucjonizowały sposób, w jaki piszemy komponenty w React, wprowadzając stan i funkcje cyklu życia do komponentów funkcyjnych. Wśród wielu korzyści, jakie oferują, własne hooki wyróżniają się jako potężny mechanizm do ekstrakcji i ponownego wykorzystywania logiki w wielu komponentach. Ten wpis na blogu zagłębi się w świat własnych hooków, badając ich korzyści, tworzenie i użycie na praktycznych przykładach.
Czym są Własne Hooki?
W skrócie, własny hook to funkcja JavaScript, która zaczyna się od słowa „use” i może wywoływać inne hooki. Pozwalają one na wydzielenie logiki komponentu do funkcji wielokrotnego użytku. Jest to potężny sposób na współdzielenie logiki stanowej, efektów ubocznych lub innych złożonych zachowań między komponentami bez uciekania się do render props, komponentów wyższego rzędu (HOC) czy innych skomplikowanych wzorców.
Kluczowe cechy Własnych Hooków:
- Konwencja nazewnictwa: Własne hooki muszą zaczynać się od słowa „use”. Sygnalizuje to Reactowi, że funkcja zawiera hooki i powinna przestrzegać ich zasad.
- Wielokrotne użycie: Głównym celem jest enkapsulacja logiki wielokrotnego użytku, co ułatwia współdzielenie funkcjonalności między komponentami.
- Logika stanowa: Własne hooki mogą zarządzać własnym stanem za pomocą hooka
useState
, co pozwala im na hermetyzację złożonych zachowań stanowych. - Efekty uboczne: Mogą również wykonywać efekty uboczne za pomocą hooka
useEffect
, umożliwiając integrację z zewnętrznymi API, pobieranie danych i wiele więcej. - Komponowalność: Własne hooki mogą wywoływać inne hooki, co pozwala na budowanie złożonej logiki poprzez komponowanie mniejszych, bardziej wyspecjalizowanych hooków.
Zalety Używania Własnych Hooków
Własne hooki oferują kilka znaczących zalet w programowaniu w React:
- Wielokrotne użycie kodu: Najbardziej oczywistą korzyścią jest możliwość ponownego wykorzystania logiki w wielu komponentach. Zmniejsza to duplikację kodu i promuje bazę kodu zgodną z zasadą DRY (Don't Repeat Yourself).
- Lepsza czytelność: Poprzez wydzielenie złożonej logiki do osobnych, własnych hooków, Twoje komponenty stają się czystsze i łatwiejsze do zrozumienia. Główna logika komponentu pozostaje skoncentrowana na renderowaniu interfejsu użytkownika.
- Łatwiejsze utrzymanie: Kiedy logika jest zamknięta we własnych hookach, zmiany i poprawki błędów można wprowadzać w jednym miejscu, co zmniejsza ryzyko wprowadzenia błędów w wielu komponentach.
- Testowalność: Własne hooki można łatwo testować w izolacji, zapewniając, że logika wielokrotnego użytku działa poprawnie, niezależnie od komponentów, które jej używają.
- Uproszczone komponenty: Własne hooki pomagają uporządkować komponenty, czyniąc je mniej rozwlekłymi i bardziej skoncentrowanymi na ich głównym celu.
Tworzenie Pierwszego Własnego Hooka
Zilustrujmy tworzenie własnego hooka na praktycznym przykładzie: hooka, który śledzi rozmiar okna.
Przykład: useWindowSize
Ten hook zwróci aktualną szerokość i wysokość okna przeglądarki. Będzie również aktualizował te wartości, gdy okno zostanie zmienione.
import { useState, useEffect } from 'react';
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener('resize', handleResize);
// Remove event listener on cleanup
return () => window.removeEventListener('resize', handleResize);
}, []); // Empty array ensures that effect is only run on mount
return windowSize;
}
export default useWindowSize;
Wyjaśnienie:
- Import niezbędnych hooków: Importujemy
useState
iuseEffect
z Reacta. - Definiowanie hooka: Tworzymy funkcję o nazwie
useWindowSize
, zgodnie z konwencją nazewnictwa. - Inicjalizacja stanu: Używamy
useState
do zainicjowania stanuwindowSize
początkową szerokością i wysokością okna. - Konfiguracja nasłuchiwania zdarzeń: Używamy
useEffect
, aby dodać nasłuchiwacz zdarzenia „resize” do obiektu window. Kiedy okno jest zmieniane, funkcjahandleResize
aktualizuje stanwindowSize
. - Czyszczenie: Zwracamy funkcję czyszczącą z
useEffect
, aby usunąć nasłuchiwacz zdarzeń, gdy komponent zostanie odmontowany. Zapobiega to wyciekom pamięci. - Zwracane wartości: Hook zwraca obiekt
windowSize
, zawierający aktualną szerokość i wysokość okna.
Używanie Własnego Hooka w Komponencie
Teraz, gdy stworzyliśmy nasz własny hook, zobaczmy, jak go używać w komponencie React.
import React from 'react';
import useWindowSize from './useWindowSize';
function MyComponent() {
const { width, height } = useWindowSize();
return (
Window width: {width}px
Window height: {height}px
);
}
export default MyComponent;
Wyjaśnienie:
- Import hooka: Importujemy własny hook
useWindowSize
. - Wywołanie hooka: Wywołujemy hook
useWindowSize
wewnątrz komponentu. - Dostęp do wartości: Destrukturyzujemy zwrócony obiekt, aby uzyskać wartości
width
iheight
. - Renderowanie wartości: Renderujemy wartości szerokości i wysokości w interfejsie użytkownika komponentu.
Każdy komponent, który używa useWindowSize
, będzie automatycznie aktualizowany, gdy zmieni się rozmiar okna.
Bardziej Złożone Przykłady
Przyjrzyjmy się kilku bardziej zaawansowanym przypadkom użycia własnych hooków.
Przykład: useLocalStorage
Ten hook pozwala na łatwe przechowywanie i pobieranie danych z local storage.
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// State to store our value
// Pass initial value to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => {
try {
// Get from local storage by key
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// If error also return initialValue
console.log(error);
return initialValue;
}
});
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = (value) => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore = value instanceof Function ? value(storedValue) : value;
// Save to local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore));
// Save state
setStoredValue(valueToStore);
} catch (error) {
// A more advanced implementation would handle the error case
console.log(error);
}
};
useEffect(() => {
try {
const item = window.localStorage.getItem(key);
setStoredValue(item ? JSON.parse(item) : initialValue);
} catch (error) {
console.log(error);
}
}, [key, initialValue]);
return [storedValue, setValue];
}
export default useLocalStorage;
Użycie:
import React from 'react';
import useLocalStorage from './useLocalStorage';
function MyComponent() {
const [name, setName] = useLocalStorage('name', 'Guest');
return (
Hello, {name}!
setName(e.target.value)}
/>
);
}
export default MyComponent;
Przykład: useFetch
Ten hook hermetyzuje logikę pobierania danych z API.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const json = await response.json();
setData(json);
setLoading(false);
} catch (error) {
setError(error);
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
Użycie:
import React from 'react';
import useFetch from './useFetch';
function MyComponent() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/todos/1');
if (loading) return Loading...
;
if (error) return Error: {error.message}
;
return (
Title: {data.title}
Completed: {data.completed ? 'Yes' : 'No'}
);
}
export default MyComponent;
Dobre Praktyki dla Własnych Hooków
Aby zapewnić, że Twoje własne hooki są skuteczne i łatwe w utrzymaniu, postępuj zgodnie z poniższymi dobrymi praktykami:
- Skupienie na jednym celu: Każdy własny hook powinien mieć jeden, dobrze zdefiniowany cel. Unikaj tworzenia zbyt skomplikowanych hooków, które próbują robić zbyt wiele.
- Dokumentowanie hooków: Dostarczaj jasną i zwięzłą dokumentację dla każdego własnego hooka, wyjaśniając jego cel, dane wejściowe i wyjściowe.
- Testowanie hooków: Pisz testy jednostkowe dla swoich własnych hooków, aby upewnić się, że działają poprawnie i niezawodnie.
- Używanie opisowych nazw: Wybieraj opisowe nazwy dla swoich własnych hooków, które jasno wskazują ich przeznaczenie.
- Obsługa błędów: Implementuj obsługę błędów w swoich własnych hookach, aby zapobiec nieoczekiwanemu zachowaniu i dostarczać informacyjne komunikaty o błędach.
- Myślenie o wielokrotnym użyciu: Projektuj swoje własne hooki z myślą o ponownym użyciu. Uczyń je na tyle generycznymi, aby można je było stosować w wielu komponentach.
- Unikanie nadmiernej abstrakcji: Nie twórz własnych hooków dla prostej logiki, którą można łatwo obsłużyć wewnątrz komponentu. Wydzielaj tylko logikę, która jest naprawdę złożona i wielokrotnego użytku.
Częste Pułapki, Których Należy Unikać
- Łamanie Zasad Hooków: Zawsze wywołuj hooki na najwyższym poziomie funkcji swojego własnego hooka i wywołuj je tylko z komponentów funkcyjnych React lub innych własnych hooków.
- Ignorowanie zależności w useEffect: Upewnij się, że uwzględniasz wszystkie niezbędne zależności w tablicy zależności hooka
useEffect
, aby zapobiec przestarzałym domknięciom (stale closures) i nieoczekiwanemu zachowaniu. - Tworzenie nieskończonych pętli: Bądź ostrożny podczas aktualizowania stanu wewnątrz hooka
useEffect
, ponieważ może to łatwo prowadzić do nieskończonych pętli. Upewnij się, że aktualizacja jest warunkowa i oparta na zmianach w zależnościach. - Zapominanie o czyszczeniu: Zawsze dołączaj funkcję czyszczącą w
useEffect
, aby usuwać nasłuchiwacze zdarzeń, anulować subskrypcje i wykonywać inne zadania porządkowe w celu zapobiegania wyciekom pamięci.
Zaawansowane Wzorce
Komponowanie Własnych Hooków
Własne hooki można komponować razem, aby tworzyć bardziej złożoną logikę. Na przykład, można połączyć hook useLocalStorage
z hookiem useFetch
, aby automatycznie zapisywać pobrane dane w local storage.
Dzielenie Logiki Między Hookami
Jeśli wiele własnych hooków dzieli wspólną logikę, można tę logikę wydzielić do osobnej funkcji pomocniczej i ponownie jej użyć w obu hookach.
Używanie Kontekstu z Własnymi Hookami
Własne hooki można używać w połączeniu z React Context, aby uzyskać dostęp i aktualizować globalny stan. Pozwala to na tworzenie komponentów wielokrotnego użytku, które są świadome globalnego stanu aplikacji i mogą z nim wchodzić w interakcje.
Przykłady z Prawdziwego Świata
Oto kilka przykładów, jak własne hooki mogą być używane w rzeczywistych aplikacjach:
- Walidacja formularza: Stwórz hook
useForm
do obsługi stanu formularza, walidacji i przesyłania. - Uwierzytelnianie: Zaimplementuj hook
useAuth
do zarządzania uwierzytelnianiem i autoryzacją użytkownika. - Zarządzanie motywami: Opracuj hook
useTheme
do przełączania między różnymi motywami (jasny, ciemny itp.). - Geolokalizacja: Zbuduj hook
useGeolocation
do śledzenia bieżącej lokalizacji użytkownika. - Wykrywanie przewijania: Stwórz hook
useScroll
do wykrywania, kiedy użytkownik przewinął stronę do określonego punktu.
Przykład: hook useGeolocation dla aplikacji międzykulturowych, takich jak mapy czy usługi dostawcze
import { useState, useEffect } from 'react';
function useGeolocation() {
const [location, setLocation] = useState({
latitude: null,
longitude: null,
error: null,
});
useEffect(() => {
if (!navigator.geolocation) {
setLocation({
latitude: null,
longitude: null,
error: 'Geolocation is not supported by this browser.',
});
return;
}
const watchId = navigator.geolocation.watchPosition(
(position) => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
},
(error) => {
setLocation({
latitude: null,
longitude: null,
error: error.message,
});
}
);
return () => navigator.geolocation.clearWatch(watchId);
}, []);
return location;
}
export default useGeolocation;
Podsumowanie
Własne hooki to potężne narzędzie do pisania czystszego, bardziej reużywalnego i łatwiejszego w utrzymaniu kodu React. Poprzez hermetyzację złożonej logiki we własnych hookach, możesz uprościć swoje komponenty, zredukować duplikację kodu i poprawić ogólną strukturę swoich aplikacji. Wykorzystaj własne hooki i odblokuj ich potencjał do budowania bardziej solidnych i skalowalnych aplikacji React.
Zacznij od zidentyfikowania w swojej istniejącej bazie kodu obszarów, w których logika jest powtarzana w wielu komponentach. Następnie zrefaktoryzuj tę logikę do własnych hooków. Z czasem zbudujesz bibliotekę hooków wielokrotnego użytku, która przyspieszy proces rozwoju i poprawi jakość Twojego kodu.
Pamiętaj, aby postępować zgodnie z najlepszymi praktykami, unikać typowych pułapek i odkrywać zaawansowane wzorce, aby w pełni wykorzystać możliwości własnych hooków. Dzięki praktyce i doświadczeniu staniesz się mistrzem własnych hooków i bardziej skutecznym programistą React.