Powrót do strony głównej

Jak używać React Hooks: Kompletny przewodnik z przykładami

Ten kompleksowy przewodnik uczy programistów, jak efektywnie używać React hooks w nowoczesnych aplikacjach React. Obejmuje podstawowe wbudowane hooki, takie jak useState i useEffect, wyjaśnia zasady hooków, przedstawia techniki optymalizacji wydajności z useMemo i useCallback oraz demonstruje tworzenie własnych hooków z praktycznymi przykładami kodu.

React Hooks Tutorial: Opanuj stan i efekty w 2026
Advertisement 728x90

Jak używać React Hooks: Kompletny przewodnik z przykładami

Jak używać React Hooks: Kompletny przewodnik z przykładami

Gdy na początku 2019 roku w React 16.8 pojawiły się Hooks, radykalnie zmieniło to podejście programistów do tworzenia aplikacji React, umożliwiając komponentom funkcyjnym zarządzanie stanem, efektami ubocznymi i kontekstem bez pisania komponentów klasowych. Zrozumienie jak efektywnie używać React Hooks jest teraz niezbędne dla każdego nowoczesnego programisty React, ponieważ Hooks stały się „jedynym sposobem pisania większości kodu React”. Ten przewodnik zapozna Cię z podstawowymi wbudowanymi Hookami, wyjaśni zasady ich używania i dostarczy praktycznych przykładów, które możesz zastosować od razu.

Czego się nauczysz

Pod koniec tego przewodnika będziesz pewnie używać podstawowych Hooków React — useState, useEffect, useContext, useRef, useReducer, useMemo i useCallback — w swoich projektach. Zrozumiesz, kiedy wybrać dany Hook, jak unikać typowych błędów, takich jak niepotrzebne przerenderowania, oraz jak wyodrębniać wielokrotnie używaną logikę do własnych Hooków. Najważniejszy wniosek: Hooks to po prostu funkcje; umiejętność ich łączenia jest kluczem do opanowania React.

Google AdInline article slot

Zrozumienie zasad używania Hooków

Zanim zagłębisz się w poszczególne Hooks, musisz przyswoić dwie niezmienne zasady, których przestrzeganie jest egzekwowane przez pakiet eslint-plugin-react-hooks:

  1. Wywołuj Hooks tylko na najwyższym poziomie. Nigdy nie wywołuj Hooków wewnątrz pętli, instrukcji warunkowych lub zagnieżdżonych funkcji. React polega na kolejności wywołań Hooków, aby zachować stan między renderowaniami — jeśli kolejność się zmieni, React nie będzie w stanie prawidłowo dopasować stanu do jego Hooka.

  2. Wywołuj Hooks tylko z funkcji React. Możesz wywoływać Hooks tylko z komponentów funkcyjnych lub z innych własnych Hooków. Zwykła funkcja JavaScript nie może wywoływać useState ani useEffect.

    Google AdInline article slot

Z drugiej zasady wynika konwencja: każda funkcja wywołująca Hooks powinna zaczynać się od use (np. useCustomHook). Linter używa tego prefiksu do prawidłowego stosowania zasad.

Najważniejsze Hooks, których będziesz używać codziennie

useState: Zarządzanie stanem komponentu

Hook useState jest najbardziej podstawowy — umożliwia dodawanie stanu do komponentów funkcyjnych. Zwraca tablicę z dwóch elementów: bieżącą wartość stanu i funkcję do jej aktualizacji.

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // Stan początkowy — 0
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Kliknięto {count} razy
    </button>
  );
}

Podczas aktualizacji stanu na podstawie poprzedniej wartości używaj funkcyjnej formy aktualizacji, aby uniknąć stanów wyścigu, szczególnie przy automatycznym grupowaniu React:

Google AdInline article slot
// Bezpiecznie: używa poprzedniej wartości stanu
setCount(prevCount => prevCount + 1);

// Również bezpiecznie dla obiektów: rozprzestrzenia poprzedni stan
setUser(prevUser => ({ ...prevUser, name: 'Jane' }));

Jeśli obliczanie stanu początkowego jest kosztowne, przekaż funkcję zamiast wartości — React wykona ją tylko przy początkowym renderowaniu:

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

useEffect: Obsługa efektów ubocznych

Hook useEffect umożliwia wykonywanie efektów ubocznych w komponentach funkcyjnych, takich jak pobieranie danych, konfigurowanie subskrypcji lub ręczna modyfikacja DOM. Domyślnie efekty uruchamiają się po każdym zakończonym renderowaniu, ale możesz kontrolować, kiedy są wyzwalane, za pomocą tablicy zależności.

import { useEffect, useState } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // Ten efekt uruchamia się po zamontowaniu komponentu i przy zmianie userId
    fetchUser(userId).then(data => setUser(data));
    
    // Opcjonalna funkcja czyszcząca uruchamia się przed następnym efektem i przy odmontowaniu
    return () => {
      // Anulowanie oczekujących zapytań lub czyszczenie subskrypcji
    };
  }, [userId]); // Tablica zależności: ponowne uruchomienie tylko przy zmianie userId

  return <div>{user?.name}</div>;
}

Kluczowe wzorce dla useEffect:

  • Pusta tablica zależności []: Efekt uruchamia się raz po początkowym renderowaniu (zamontowaniu).
  • Bez tablicy zależności: Efekt uruchamia się po każdym renderowaniu (rzadko potrzebne).
  • Funkcja czyszcząca: Zapobiega wyciekom pamięci, czyszcząc subskrypcje, timery lub oczekujące zapytania sieciowe.

Ważne ostrzeżenie: Nie każda zmiana stanu wymaga efektu. Jeśli możesz obliczyć wartość podczas renderowania, zrób to bezpośrednio, a nie przez useEffect:

// ❌ Niepotrzebny efekt
const [fullName, setFullName] = useState('');
useEffect(() => {
  setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);

// ✅ Obliczenie bezpośrednio podczas renderowania
const fullName = `${firstName} ${lastName}`;

useContext: Korzystanie z kontekstu bez przeciągania propsów

Hook useContext umożliwia odczytywanie wartości z React Context bez owijania komponentu w Consumer:

import { useContext } from 'react';

// Tworzenie kontekstu
const ThemeContext = React.createContext('light');

function ThemedButton() {
  // Bezpośrednie odczytanie wartości kontekstu
  const theme = useContext(ThemeContext);
  return <button className={theme}>Kliknij mnie</button>;
}

Gdy wartość providera kontekstu się zmieni, każdy komponent używający useContext automatycznie się przerenderowuje.

useRef: Referencje do wartości, które nie powodują przerenderowania

Hook useRef tworzy mutowalny obiekt ref, którego właściwość .current jest zachowywana między renderowaniami. W przeciwieństwie do stanu, aktualizacja ref nie powoduje przerenderowania. To sprawia, że refs są idealne do:

  • Przechowywania referencji do elementów DOM
  • Śledzenia identyfikatorów timerów
  • Przechowywania wartości, które nie powinny wywoływać aktualizacji UI
import { useRef, useEffect } from 'react';

function TextInput() {
  const inputRef = useRef(null);

  useEffect(() => {
    // Ustawienie fokusu na polu wejściowym po zamontowaniu komponentu
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} type="text" />;
}

useReducer: Zarządzanie złożoną logiką stanu

Dla stanu obejmującego wiele podwartości lub złożone przejścia, useReducer jest często lepszy niż useState. Przyjmuje funkcję reducer i stan początkowy, zwracając bieżący stan i funkcję dispatch:

import { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return { count: action.payload };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  
  return (
    <>
      Licznik: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Reset</button>
    </>
  );
}

Hooks do optymalizacji wydajności

useMemo: Buforowanie kosztownych obliczeń

useMemo buforuje wynik obliczenia i przelicza go tylko przy zmianie zależności:

import { useMemo } from 'react';

function TodoList({ todos, filter }) {
  // Przeliczenie tylko przy zmianie todos lub filter
  const filteredTodos = useMemo(() => {
    return todos.filter(todo => todo.status === filter);
  }, [todos, filter]);

  return (
    <ul>
      {filteredTodos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

useCallback: Memoizacja referencji do funkcji

useCallback zwraca zmemoizowaną wersję funkcji zwrotnej, która zmienia się tylko przy zmianie zależności. Jest to szczególnie przydatne przy przekazywaniu callbacków do zoptymalizowanych komponentów potomnych, które polegają na równości referencyjnej, aby zapobiec niepotrzebnym przerenderowaniom:

import { useCallback } from 'react';

function ParentComponent() {
  const handleClick = useCallback(() => {
    console.log('Przycisk kliknięty');
  }, []); // Referencja do funkcji pozostaje stabilna między renderowaniami

  return <ChildButton onClick={handleClick} />;
}

Własne Hooks: Ponowne wykorzystanie logiki między komponentami

Prawdziwa moc Hooków ujawnia się przy tworzeniu własnych Hooków do wyodrębniania i ponownego wykorzystywania logiki ze stanem. Własny Hook to po prostu funkcja JavaScript, której nazwa zaczyna się od use i która może wywoływać inne Hooks.

Przykład: useLocalStorage

Ten własny Hook synchronizuje stan z localStorage, zachowując dane po odświeżeniu strony:

import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    if (typeof window === 'undefined') return initialValue;
    const stored = localStorage.getItem(key);
    return stored !== null ? JSON.parse(stored) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

// Użycie
function ThemeToggle() {
  const [theme, setTheme] = useLocalStorage('theme', 'light');
  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Przełącz na {theme === 'light' ? 'ciemny' : 'jasny'} tryb
    </button>
  );
}

Przykład: useMediaQuery

Ten Hook umożliwia warunkowe renderowanie komponentów na podstawie zapytań medialnych CSS, np. preferencji użytkownika dotyczących ograniczenia ruchu:

import { useState, useEffect } from 'react';

function useMediaQuery(query) {
  const [matches, setMatches] = useState(() => {
    if (typeof window === 'undefined') return false;
    return window.matchMedia(query).matches;
  });

  useEffect(() => {
    const mediaQueryList = window.matchMedia(query);
    const onChange = (event) => setMatches(event.matches);
    mediaQueryList.addEventListener('change', onChange);
    return () => mediaQueryList.removeEventListener('change', onChange);
  }, [query]);

  return matches;
}

// Użycie
function MotionAwareComponent() {
  const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)');
  return prefersReducedMotion ? <StaticHero /> : <AnimatedHero />;
}

React 19: Nowe Hooks dla formularzy i danych asynchronicznych

React 19 (wydany w grudniu 2024 roku) wprowadził kilka nowych Hooków przeznaczonych do uproszczenia pracy z formularzami i operacjami asynchronicznymi.

useActionState: Uproszczenie wysyłania formularzy

useActionState zarządza stanem wysyłania formularza, zwracając ostatni wynik, funkcję akcji i flagę oczekiwania:

import { useActionState } from 'react';

async function subscribeAction(prevState, formData) {
  const email = formData.get('email');
  try {
    await subscribe(email);
    return { ok: true };
  } catch (error) {
    return { ok: false, error: error.message };
  }
}

function NewsletterForm() {
  const [state, formAction, isPending] = useActionState(
    subscribeAction,
    { ok: false }
  );

  return (
    <form action={formAction}>
      <input name="email" type="email" required />
      <button type="submit" disabled={isPending}>
        {isPending ? 'Subskrypcja...' : 'Subskrybuj'}
      </button>
      {state.ok && <p>Jesteś zapisany!</p>}
      {state.error && <p style={{ color: 'red' }}>{state.error}</p>}
    </form>
  );
}

useFormStatus: Dostęp do stanu formularza nadrzędnego

useFormStatus umożliwia zagnieżdżonym komponentom sprawdzenie, czy ich formularz nadrzędny jest wysyłany:

import { useFormStatus } from 'react';

function SubmitButton() {
  const { pending } = useFormStatus();
  return (
    <button type="submit" disabled={pending}>
      {pending ? 'Zapisywanie...' : 'Zapisz'}
    </button>
  );
}

Integracja z Redux: Hook useSelector

Podczas używania Redux z React, Hook useSelector jest nowoczesnym zalecanym sposobem dostępu do stanu z magazynu Redux. Zastępuje on stary komponent wyższego rzędu connect i lepiej współpracuje z TypeScript.

import { useSelector, useDispatch } from 'react-redux';

function CounterComponent() {
  // Wyodrębnienie konkretnych danych z magazynu Redux
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <span>{count}</span>
      <button onClick={() => dispatch(increment())}>Zwiększ</button>
    </div>
  );
}

Ważne: useSelector domyślnie używa ścisłego porównania referencyjnego ===. Zwracanie nowego obiektu za każdym razem spowoduje przerenderowania. Używaj zmemoizowanych selektorów lub wywołuj useSelector wielokrotnie dla poszczególnych pól, aby zoptymalizować wydajność:

// ❌ To powoduje przerenderowania przy każdej akcji
const { count, user } = useSelector((state) => ({
  count: state.count,
  user: state.user
}));

// ✅ Wywołuj useSelector dla każdego pola
const count = useSelector((state) => state.count);
const user = useSelector((state) => state.user);

Źródła

  • Oficjalna dokumentacja React: Wbudowane React Hooks
  • Oficjalna dokumentacja React: Podręcznik API Hooks
  • Dokumentacja React Redux: API Hooks
  • Patterns.dev: Wzorzec Hooks
  • Oficjalna dokumentacja React: Używanie State Hook

— Editorial Team

Advertisement 728x90

Czytaj dalej