React Hooks — co to jest, podstawowe hooki i zasady używania

Autor: IT Sectr Opublikowano: 2026-07-04 Czas czytania: 10 min

Hooki React — to funkcje, które umożliwiają używanie stanu, efektów, kontekstu i innych możliwości React w komponentach funkcyjnych, bez pisania klas. Wprowadzone w React 16.8, hooki radykalnie zmieniły podejście do programowania, zastępując złożone wzorce, takie jak HOC i render props. Według React, 2024, istnieje 15 wbudowanych hooków, pokrywających wszystkie scenariusze zarządzania komponentem.

Najważniejsze

  • Hooki — funkcje do łączenia możliwości React z komponentami funkcyjnymi bez extends Component.
  • useState — podstawowy hook do zarządzania stanem, zwraca wartość i setter.
  • useEffect — do efektów ubocznych: zapytania API, subskrypcje, timery, manipulacje DOM.
  • useContext — do dostępu do kontekstu React bez opakowań Consumer i static contextType.
  • Zasady hooków — wywoływać tylko na najwyższym poziomie i tylko wewnątrz komponentów funkcyjnych lub własnych hooków.

Czym są hooki React?

Hooki React — zestaw funkcji wbudowanych w React, które umożliwiają „podłączanie się” do wewnętrznych mechanizmów frameworka bez tworzenia klas. Przed React 16.8 komponenty funkcyjne mogły tylko przyjmować props i zwracać JSX — każda złożona logika wymagała komponentu klasowego.

Problem komponentów klasowych — rozdzielenie powiązanej logiki. Kod odnoszący się do jednej jednostki (na przykład subskrypcja WebSocket) był wymuszenie podzielony między componentDidMount, componentDidUpdate i componentWillUnmount. Hooki rozwiązują to: cała logika grupuje się w jednym useEffect.

Drugi problem — ponowne użycie logiki. Do wielokrotnego użycia logiki stanu między komponentami klasowymi trzeba było stosować HOC (komponenty wyższego rzędu) lub render props — oba wzorce tworzyły dodatkowe zagnieżdżenie w drzewie komponentów. Własne hooki rozwiązują ten problem bez zagnieżdżania.

useState — zarządzanie stanem

useState — najbardziej podstawowy hook. Przyjmuje wartość początkową i zwraca krotkę: bieżącą wartość i funkcję do jej aktualizacji. W przeciwieństwie do this.setState w klasach, useState nie scala obiektów — nowy stan całkowicie zastępuje poprzedni dla konkretnego wywołania useState.

js
import React, { useState } from 'react';

function Form() {
  const [text, setText] = useState('');
  const [submitting, setSubmitting] = useState(false);

  async function handleSubmit() {
    setSubmitting(true);
    await fetch('/api/submit', {
      method: 'POST',
      body: JSON.stringify({ text })
    });
    setSubmitting(false);
  }

  return (
    <div>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button onClick={handleSubmit}
        disabled={submitting}>
        {submitting ? 'Saving...' : 'Submit'}
      </button>
    </div>
  );
}

Funkcyjna forma setState

Setter useState może przyjmować funkcję, która otrzymuje poprzednią wartość i zwraca nową. Funkcyjna forma jest obowiązkowa, gdy nowy stan jest obliczany z poprzedniego i możliwe są batczowane wywołania setState — każde wywołanie otrzyma aktualną poprzednią wartość.

useEffect — efekty uboczne i cykl życia

useEffect — hook do wykonywania efektów ubocznych w komponentach funkcyjnych. Przyjmuje dwa argumenty: funkcję-efekt i tablicę zależności. React wykonuje efekt po każdym renderze, ale przy określeniu zależności — tylko gdy którakolwiek z nich się zmieniła.

Pusta tablica zależności [] oznacza, że efekt wykonuje się raz po pierwszym renderze (odpowiednik componentDidMount). Zwrócenie funkcji z efektu to cleanup, który wykonuje się przy odmontowywaniu (odpowiednik componentWillUnmount) i przed ponownym wykonaniem efektu.

js
import React, { useState, useEffect } from 'react';

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = new WebSocket(`wss://chat/${roomId}`);

    socket.onmessage = (event) => {
      setMessages(prev => [...prev, JSON.parse(event.data)]);
    };

    // Cleanup - zamknij gniazdo
    return () => socket.close();
  }, [roomId]);

  return <ul>
    {messages.map((msg, i) =>
      <li key={i}>{msg.text}</li>
    )}
  </ul>;
}

Zależności useEffect — to jedyne połączenie między efektem a światem zewnętrznym. Jeśli w efekcie używana jest zmienna z propsów lub stanu, musi być w tablicy zależności. Ignorowanie tej zasady jest źródłem błędów z nieaktualnymi danymi (stale closure).

useContext — dostęp do kontekstu

useContext — hook do odczytywania wartości kontekstu. Przyjmuje obiekt kontekstu utworzony przez React.createContext i zwraca bieżącą wartość. Jeśli kontekst się zmienia, komponent automatycznie przerenderowuje się z nową wartością.

Przed pojawieniem się useContext dostęp do kontekstu w komponentach funkcyjnych wymagał opakowania w Context.Consumer, co tworzyło zagnieżdżenie. useContext całkowicie eliminuje Consumer — wystarczy wywołać hook w dowolnym miejscu komponentu funkcyjnego.

js
import React, { createContext, useContext } from 'react';

const ThemeContext = createContext('jasny');

function ThemedButton() {
  const theme = useContext(ThemeContext);

  return <button
    className={`btn btn-${theme}`}
  >
    {theme === 'dark' ? 'moon' : '☀️'}
  </button>;
}

useContext nie jest zamiennikiem Redux ani globalnego stanu. React zaleca używanie kontekstu dla rzadko zmieniających się wartości — motyw, język, autoryzacja. Dla często aktualizowanych danych każdy re-render będzie ponownie obliczał wszystkich konsumentów kontekstu, co może obniżyć wydajność.

useReducer — złożona logika stanu

useReducer — alternatywa dla useState dla stanów ze złożoną logiką aktualizacji. Przyjmuje funkcję reducer i stan początkowy, zwraca bieżący stan i funkcję dispatch. Reducer otrzymuje bieżący stan i akcję, zwraca nowy stan — czysta funkcja bez efektów ubocznych.

useReducer jest szczególnie przydatny, gdy kilka akcji (set, reset, increment, addItem) zarządza jednym stanem. Zamiast wielu useState z różnymi setterami — jeden reducer z wyraźnymi typami akcji. To czyni logikę przewidywalną i łatwą do testowania.

js
import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 };
    case 'setStep':
      return { ...state, step: action.step };
    case 'reset':
      return { count: 0, step: 1 };
    default:
      throw new Error('Unknown action: ' + action.type);
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, {
    count: 0,
    step: 1
  });

  return (
    <>
      <p>Count: {state.count}</p>
      <button
        onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button
        onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </>
  );
}

useRef, useMemo, useCallback — optymalizacja

useRef tworzy zmienialny obiekt, który utrzymuje się między renderami. Zmiana current nie powoduje ponownego renderowania. Główne zastosowanie — dostęp do elementów DOM i przechowywanie wartości mutowalnych (timery, poprzednie wartości, instancje bibliotek).

useMemo memoizuje wynik funkcji. Przelicza wartość tylko gdy zmienia się jedna z zależności. Używaj do kosztownych obliczeń — filtrowanie dużych tablic, sortowanie, formatowanie danych. useCallback — szczególny przypadek useMemo do memoizacji funkcji.

js
import React, { useRef, useMemo, useCallback } from 'react';

function SearchPage({ items, query, onItemSelect }) {
  const inputRef = useRef(null);

  // Autofokus przy montowaniu
  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  // Zapamiętaj przefiltrowany wynik
  const filtered = useMemo(() => {
    return items.filter(item =>
      item.name.toLowerCase()
        .includes(query.toLowerCase())
    );
  }, [items, query]);

  // Zapamiętaj callback
  const handleSelect = useCallback((id) => {
    onItemSelect(id);
  }, [onItemSelect]);

  return (
    <div>
      <input ref={inputRef}
        value={query}
        onChange={e => setQuery(e.target.value)}
      />
      <ul>
        {filtered.map(item =>
          <li key={item.id}>
            <button
              onClick={() => handleSelect(item.id)}>
              {item.name}
            </button>
          </li>
        )}
      </ul>
    </div>
  );
}

Ważna zasada: useRef, useMemo i useCallback — narzędzia optymalizacji, a nie obowiązkowe konstrukcje. Nie opakowuj wszystkiego w useMemo — najpierw zmierz wydajność. Przedwczesna optymalizacja komplikuje kod. React renderuje komponenty szybko; useMemo jest potrzebny tylko gdy obliczenia są naprawdę kosztowne (O(n²) i wyżej).

Zasady hooków i częste błędy

React wprowadził dwie sztywne zasady dla hooków. Pierwsza: wywołuj hooki tylko na najwyższym poziomie — nie wewnątrz warunków, pętli ani zagnieżdżonych funkcji. Druga: wywołuj hooki tylko z komponentów funkcyjnych React lub własnych hooków. ESLint-plugin eslint-plugin-react-hooks automatycznie sprawdza obie zasady.

Najczęstszy błąd — naruszenie kolejności wywoływania hooków. React polega na kolejności wywołania hooków między renderami. Jeśli hook znajduje się wewnątrz warunku i nie został wywołany w jednym z renderów, wszystkie następne hooki przesuwają się, powodując błędy.

BłądCo się dziejeRozwiązanie
Hook w warunkuKolejność hooków się łamiePrzenieś logikę warunku do wewnątrz hooka
Stale closureEfekt używa starej wartościDodaj wszystkie zależności do tablicy
Nieskończona pętlauseEffect bez zależności zmienia stateOkreśl zależności lub użyj useReducer
Modyfikacja ref w renderEfekt uboczny w ciele funkcjiPrzenieś mutacje do useEffect

Drugi częsty błąd — stale closure w useEffect. Jeśli w efekcie używana jest zmienna nieokreślona w zależnościach, efekt „chwyta” jej wartość w momencie tworzenia domknięcia. Rozwiązanie — zawsze uwzględniaj wszystkie używane zmienne w tablicy zależności. Jeśli zależności jest zbyt wiele — można podzielić efekt na kilka.

Często zadawane pytania

Dlaczego hooków nie można wywoływać w warunkach?

React polega na kolejności wywołania hooków między renderami. Jeśli hook został wywołany w warunku i nie zadziałał w jednym z renderów, wszystkie następne hooki „przesuwają się” — ich stan się miesza. Reguła ESLint hooks/exhaustive-deps ostrzega przed tym.

Jaka jest różnica między useEffect i useLayoutEffect?

useLayoutEffect jest synchroniczny i wykonuje się przed tym, jak przeglądarka narysuje zmiany. useEffect jest asynchroniczny — po narysowaniu. Używaj useLayoutEffect tylko gdy trzeba zmierzyć lub zmienić DOM zanim użytkownik zobaczy zmiany (pozycja popover, scroll restoration).

Jak anulować zapytanie fetch przy odmontowywaniu?

Użyj AbortController wewnątrz useEffect. Utwórz controller, przekaż signal do fetch, a w funkcji cleanup wywołaj controller.abort(). Dla starszych przeglądarek wystarczy flaga cancelled: return () => { cancelled = true; } z sprawdzeniem przed setState.

Czym jest własny hook i kiedy go tworzyć?

Własny hook — funkcja JavaScript z prefiksem use, używająca wbudowanych hooków. Twórz go, gdy ta sama logika z hookami powtarza się w dwóch lub więcej komponentach. Przykłady: useDebounce, useLocalStorage, useMediaQuery. Własne hooki zastępują HOC i render props.

Dlaczego useEffect wykonuje się dwukrotnie w Strict Mode?

W Strict Mode React celowo montuje i odmontowuje komponent dwukrotnie w trybie deweloperskim, aby znaleźć błędy z nieoczyszczonymi efektami. Jeśli cleanup jest napisany poprawnie, podwójne wywołanie nie powoduje problemów. W trybie produkcyjnym to nie występuje.

Podsumowanie

  • Hooki React — funkcje do używania stanu, efektów i kontekstu w komponentach funkcyjnych, które zastąpiły klasy.
  • useState — podstawowy hook dla lokalnego stanu z funkcyjnym setterem do bezpiecznych aktualizacji.
  • useEffect — uniwersalny hook dla efektów ubocznych z tablicą zależności i funkcją cleanup.
  • useContext — bezpośredni dostęp do kontekstu React bez opakowań Consumer i dodatkowego zagnieżdżania.
  • useReducer — do złożonej logiki stanu z typami akcji, alternatywa dla Redux w ramach jednego komponentu.
  • useRef, useMemo, useCallback — narzędzia optymalizacji: odniesienia do DOM, memoizacja wartości i callbacków.
  • Zasada najwyższego poziomu — ścisłe ograniczenie wywoływania hooków poza warunkami, pętlami i zagnieżdżonymi funkcjami.

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również