We want to hear from you!Take our 2021 Community Survey!
Ta witryna nie jest już aktualizowana.Przejdź do react.dev

Hooki - FAQ

These docs are old and won’t be updated. Go to react.dev for the new React docs.

The new documentation pages teaches React with Hooks.

Hooki są nowym dodatkiem do Reacta w wersji 16.8. Pozwalają na użycie stanu i innych funkcji Reacta bez konieczności pisania klas.

Ta strona odpowiada na najczęściej zadawane pytania odnośnie hooków.

Strategia wdrażania

Które wersje Reacta wspierają hooki?

Zaczynając od wersji 16.8.0, React zawiera stabilną implementację hooków dla:

  • React DOM
  • React Native
  • React DOM Server
  • React Test Renderer
  • React Shallow Renderer

Zauważ, że aby włączyć hooki, wszystkie paczki Reacta muszą mieć wersję 16.8.0 lub wyższą. Hooki nie zadziałają, jeżeli zapomnisz zaktualizować, na przykład, React DOM.

React Native wspiera hooki od wersji 0.59.

Czy muszę przepisać wszystkie komponenty klasowe?

Nie. Nie ma planów na usunięcie klas z Reacta — wszyscy musimy stale dostarczać nasze produkty i nie możemy sobie pozwolić na ich przepisywanie. Zachęcamy do wypróbowania hooków w nowym kodzie.

Co mogę zrobić z hookami, czego nie można było zrobić z klasami?

Hooki oferują nowy, potężny i ekspresyjny sposób na wielokrotne używanie funkcjonalności w komponentach. Rozdział pt. “Tworzenie własnych hooków” zawiera szybki wgląd w to, co można za ich pomocą zrobić. Ten artykuł, napisany przez jednego z głównych członków zespołu Reacta, zawiera bardziej szczegółowe informacje o nowych możliwościach, które pojawiły się wraz z hookami.

Jaka część mojej wiedzy o Reakcie jest nadal aktualna?

Hooki są bardziej bezpośrednim sposobem na użycie dobrze już znanych funkcjonalności Reacta, takich jak na przykład: stan, cykl życia (ang. lifecycle), kontekst i referencje (ang. refs). Nie zmieniają podstaw działania Reacta, dlatego też twoja wiedza na temat komponentów, właściwości (ang. props) i przepływu danych z góry w dół pozostaje ciągle aktualna.

Hooki, same w sobie, posiadają pewną krzywą uczenia się. Jeżeli brakuje czegoś w tej dokumentacji, zgłoś problem, a my postaramy się pomóc.

Czy lepiej używać hooków, klas, czy może mieszać obydwa sposoby?

Zachęcamy do wypróbowania hooków w nowych komponentach. Upewnij się, że wszyscy z twojego zespołu wiedzą, jak ich używać i są zapoznani z tą dokumentacją. Nie zalecamy przepisywania istniejących klas na hooki, chyba że z jakiegoś powodu i tak mieliście to w planach (na przykład w celu naprawy istniejących błędów).

Nie możesz używać hooków wewnątrz komponentów klasowych, jednakże bez obaw możesz mieszać komponenty klasowe i funkcyjne z hookami w tym samym drzewie. To, czy komponent jest klasowy, czy funkcyjny i używa hooków, jest detalem implementacyjnym tego komponentu. W dłuższej perspektywie oczekujemy, że hooki będą głównym sposobem pisania komponentów reactowych.

Czy hooki obejmują wszystkie przypadki użycia, które są dostępne dla klas?

Naszym celem dla hooków jest zapewnienie wszystkich przypadków użycia klas, tak szybko jak to tylko możliwe. Brakuje jeszcze odpowiedników dla kilku rzadziej używanych metod cyklu życia komponentu, takich jak getSnapshotBeforeUpdate, getDerivedStateFromError i componentDidCatch, ale zamierzamy je wkrótce dodać.

Czy hooki zastępują “właściwości renderujące” i komponenty wyższego rzędu?

Zazwyczaj właściwości renderujace i komponenty wyższego rzędu renderują tylko pojedynczy komponent potomny. Sądzimy, że hooki są prostszym sposobem na obsługę tego przypadku użycia. Nadal jest miejsce dla obu wzorców (dla przykładu, wirtualny komponent do obsługi suwaka może mieć właściwość renderItem, a prezentacyjny komponent kontenera może mieć swoją własną strukturę DOM). Jednak w większości przypadków hooki w zupełności wystarczą, a przy okazji pomogą zmniejszyć liczbę zagnieżdżeń w drzewie.

Możesz używać tych samych API, co do tej pory - będą nadal działać.

React Redux od wersji v7.1.0 posiada wsparcie dla API hooków i udostępnia takie funkcje, jak useDispatch czy useSelector.

React Router wspiera hooki od wersji 5.1.

W przyszłości być może także inne biblioteki zaczną wspierać hooki.

Czy hooki współpracują ze statycznym typowaniem?

Hooki zostały zaprojektowane z myślą o statycznym typowaniu. Dzięki temu, że są funkcjami, łatwiej jest je poprawnie otypować, w odróżnieniu od wzorców takich jak komponenty wyższego rzędu. Najnowsze definicje Reacta dla Flow i TypeScriptu wspierają hooki.

Co ważne, przy pomocy bardziej restrykcyjnych typów możesz ograniczyć API Reacta we własnych hookach. React dostarcza podstawowe elementy, ale możesz je łączyć na różne sposoby, odmienne od tych, które zawarliśmy w standardzie.

Jak testować komponenty, które używają hooków?

Z punktu widzenia Reacta komponent wykorzystujący hooki jest zwyczajnym komponentem. Jeżeli twoje narzędzie do testów nie opiera się na wewnętrznej implementacji Reacta, to testowanie komponentów, które używają hooków, nie powinno różnić się od tego, co robisz zazwyczaj.

Uwaga

W rozdziale pt. “Testy: Przykłady i dobre praktyki” znajdziesz wiele przykładów gotowych do użycia.

Dla przykładu, załóżmy, że mamy komponent licznika:

function Example() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `Kliknięto ${count} razy`;
  });
  return (
    <div>
      <p>Kliknięto {count} razy</p>
      <button onClick={() => setCount(count + 1)}>
        Kliknij mnie
      </button>
    </div>
  );
}

Przetestujemy go używając React DOM. Aby upewnić się, że zachowanie komponentu odzwierciedla to w przeglądarce, opakujemy kod renderujący i aktualizujący w funkcję ReactTestUtils.act():

import React from 'react';
import ReactDOM from 'react-dom/client';
import { act } from 'react-dom/test-utils';import Counter from './Counter';

let container;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  document.body.removeChild(container);
  container = null;
});

it('potrafi wyrenderować i zaktualizować licznik', () => {
  // Testuje pierwsze renderowanie i efekt
  act(() => {    ReactDOM.createRoot(container).render(<Counter />);  });  const button = container.querySelector('button');
  const label = container.querySelector('p');
  expect(label.textContent).toBe('Kliknięto 0 razy');
  expect(document.title).toBe('Kliknięto 0 razy');

  // Testuje drugie renderowanie i efekt
  act(() => {    button.dispatchEvent(new MouseEvent('click', {bubbles: true}));  });  expect(label.textContent).toBe('Kliknięto 1 razy');
  expect(document.title).toBe('Kliknięto 1 razy');
});

Wywołanie funkcji act() opróżni bufor efektów znajdujących się wewnątrz.

Jeżeli musisz przetestować własny hook, możesz stworzyć komponent w teście i wywołać ten hook w ciele jego funkcji. Następnie możesz napisać test do stworzonego w ten sposób komponentu.

Aby zmniejszyć powtarzalność kodu, zalecamy użyć biblioteki react-testing-library. Została ona zaprojektowana tak, aby zachęcać do pisania testów używających komponentów w sposób podobny do zachowania docelowych użytkowników aplikacji.

Po więcej informacji zajrzyj do rozdziału pt. “Testy: Przykłady i dobre praktyki.

Co dokładnie narzucają reguły lintera?

Stworzyliśmy wtyczkę do ESLinta, która zmusza do przestrzegania zasad hooków w celu uniknięcia potencjalnych błędów. Zakładają one, że każda funkcja zaczynająca się od ”use” i zaraz po tym wielkiej litery jest hookiem. Zdajemy sobie sprawę, że ta heurystyka nie jest idealna i może wywołać wiele fałszywych alarmów. Ale bez wprowadzenia wspólnej dla całego ekosystemu konwencji, nie ma możliwości, aby hooki działały poprawnie — dłuższe nazwy zniechęcą ludzi do używania hooków lub do przestrzegania tej konwencji.

W szczególności, reguły te wymuszają, aby:

  • Wywołania hooków znajdowały się wewnątrz funkcji pisanej stylem PascalCase (zakładają, że jest to komponent) lub innej funkcji useSomething (zakładają, że jest to własny hook).
  • Hooki przy każdym renderowaniu są wywoływane w tej samej kolejności.

Jest jeszcze kilka innych heurystyk i mogą się one z czasem zmienić, gdy dostroimy reguły tak, aby zbalansować wyszukiwanie błędów i zmniejszyć liczbę fałszywych alarmów.

Od klas do hooków

Jak wyglądają metody cyklu życia w odniesieniu do hooków?

  • constructor: Komponenty funkcyjne nie potrzebują konstruktora. Stan jest inicjalizowany poprzez wywołanie useState. Jeżeli obliczenie stanu początkowego jest kosztowne obliczeniowo, możesz do useState przekazać funkcję.
  • getDerivedStateFromProps: Zamiast tego zaplanuj aktualizację podczas renderowania.
  • shouldComponentUpdate: Spójrz na React.memo poniżej.
  • render: Jest to ciało komponentu funkcyjnego.
  • componentDidMount, componentDidUpdate, componentWillUnmount: Hook useEffect może z powodzeniem zastąpić wszelkie kombinacje tych metod (włączając w to mniej znane przypadki).
  • getSnapshotBeforeUpdate, componentDidCatch i getDerivedStateFromError: W tej chwili nie istnieje hook odzwierciedlający działanie tych metod, ale zostanie wkrótce dodany.

Jak mogę pobrać dane wykorzystując hooki?

Tutaj znajdziesz małe demo, które w tym pomoże. Aby dowiedzieć się więcej, przeczytaj artykuł o pobieraniu danych z wykorzystaniem hooków.

Czy istnieje coś podobnego do zmiennych instancji?

Tak! Hook useRef() nie służy tylko do przechowywania referencji DOM. Obiekt “ref” jest generycznym kontenerem, którego właściwość current jest zmienna i może przechowywać każdą wartość, tak samo jak właściwości instancji w klasach.

Możesz do niej coś zapisać z wnętrza useEffect:

function Timer() {
  const intervalRef = useRef();
  useEffect(() => {
    const id = setInterval(() => {
      // ...
    });
    intervalRef.current = id;    return () => {
      clearInterval(intervalRef.current);
    };
  });

  // ...
}

Jeżeli chcielibyśmy po prostu ustawić interwał, nie potrzebowalibyśmy referencji (id mogłoby być lokalne dla efektu), jednakże jest to użyteczne w przypadku, gdy chcielibyśmy wyczyścić interwał z wnętrza procedury obsługi zdarzenia:

  // ...
  function handleCancelClick() {
    clearInterval(intervalRef.current);  }
  // ...

Działanie referencji jest takie samo jak użycie zmiennych instancji w klasie. Jeśli nie korzystasz z leniwej inicjalizacji, unikaj używania referencji podczas renderowania — może to prowadzić do niepożądanych zachowań. Zamiast tego modyfikuj referencje wewnątrz efektów lub procedur obsługi zdarzeń.

Lepiej używać jednej czy wielu zmiennych stanu?

Jeżeli na co dzień piszesz komponenty klasowe, kuszące może okazać się wywoływanie useState() jednokrotnie i umieszczanie całego stanu wewnątrz pojedynczego obiektu. Jeżeli chcesz, możesz tak robić. Poniżej znajdziesz przykład komponentu, który śledzi ruchy kursora. Jego pozycja i stan są trzymane w lokalnym stanie:

function Box() {
  const [state, setState] = useState({ left: 0, top: 0, width: 100, height: 100 });
  // ...
}

Teraz przyjmimy, że chcemy napisać logikę, która zmienia left i top, kiedy użytkownik ruszy myszką. Zauważ, że musimy ręcznie scalać te pola z poprzednim obiektem stanu:

  // ...
  useEffect(() => {
    function handleWindowMouseMove(e) {
      // Rozszczepienie "...state" zapewnia, że nie "stracimy" szerokości i wysokości      setState(state => ({ ...state, left: e.pageX, top: e.pageY }));    }
    // Uwaga: ta implementacja jest dość uproszczona
    window.addEventListener('mousemove', handleWindowMouseMove);
    return () => window.removeEventListener('mousemove', handleWindowMouseMove);
  }, []);
  // ...

Gdy aktualizujemy zmienną stanu, zamieniamy jej wartość. Różni się to od this.setState w klasach, które scala zaktualizowane pola do obiektu stanu.

Jeżeli tęsknisz za automatycznym scalaniem, możesz napisać własny hook useLegacyState, który scala aktualizacje obiektu stanu. Jednak zalecamy podzielenie stanu na wiele zmiennych stanu, bazując na tym, które wartości mają tendencję do zmieniania się jednocześnie.

Dla przykładu, możemy podzielić stan naszego komponentu na obiekty position oraz size i zawsze nadpisywać wartość position, bez konieczności scalania stanu z poprzednim:

function Box() {
  const [position, setPosition] = useState({ left: 0, top: 0 });  const [size, setSize] = useState({ width: 100, height: 100 });

  useEffect(() => {
    function handleWindowMouseMove(e) {
      setPosition({ left: e.pageX, top: e.pageY });    }
    // ...

Oddzielanie niezależnych zmiennych stanu ma także inną zaletę. Pozwala w przyszłości łatwo wyodrębnić powiązaną logikę do własnego hooka, na przykład:

function Box() {
  const position = useWindowPosition();  const [size, setSize] = useState({ width: 100, height: 100 });
  // ...
}

function useWindowPosition() {  const [position, setPosition] =