Wprowadzenie do hooków
Hooki są nowym dodatkiem w Reakcie 16.8. Pozwalają one używać stanu i innych funkcjonalności Reacta, bez użycia klas.
import React, { useState } from 'react';
function Example() {
// Zadeklaruj nową zmienną stanu, którą nazwiemy „count” const [count, setCount] = useState(0);
return (
<div>
<p>Naciśnięto {count} razy</p>
<button onClick={() => setCount(count + 1)}>
Naciśnij mnie
</button>
</div>
);
}
Funkcja useState
jest pierwszym „hookiem”, o którym będziemy się uczyć. Przykład ten jest jednak zaledwie zwiastunem. Nie przejmuj się, jeżeli nie ma to jeszcze większego sensu!
W kolejnym rozdziale możesz rozpocząć naukę o hookach. Tutaj wyjaśnimy, dlaczego dodaliśmy hooki do Reacta i w jaki sposób pomogą ci one w pisaniu wspaniałych aplikacji.
Uwaga
React 16.8.0 jest pierwszą wersją, która wspiera hooki. Podczas aktualizacji nie zapomnij zaktualizować wszystkich paczek, w tym React DOM. React Native wspiera hooki od wersji 0.59.
Wprowadzenie wideo
Podczas konferencji „React Conf 2018” Sophie Alpert i Dan Abramov zaprezentowali po raz pierwszy hooki. Następnie Ryan Florence zademonstrował, jak przepisać swoją aplikację, by móc ich używać. Wideo z konferencji zamieściliśmy poniżej:
Bez krytycznych zmian
Zanim przejdziemy dalej, zauważ że hooki są:
- Całkowicie opcjonalne. Możesz wypróbować hooki w kilku komponentach, bez przepisywania istniejącego kodu. Jeżeli jednak nie masz ochoty, nie musisz ich jeszcze stosować ani uczyć się o nich.
- 100% kompatybilne wstecznie. Hooki nie zawierają żadnych zmian, które mogłyby zepsuć istniejący kod.
- Dostępne już teraz. Hooki są dostępne od wersji 16.8.0.
Nie ma planów na usunięcie klas z Reacta. Możesz przeczytać o strategii stopniowego wdrażania hooków w kolejnym podrozdziale tej strony.
Hooki nie zastępują twojej wiedzy na temat Reacta. Zamiast tego wprowadzają bardziej bezpośredni interfejs API dla mechanizmów Reacta, które już znasz: właściwości (ang. props), stanu, kontekstu, referencji (ang. refs) i cyklu życia (ang. lifecycle). Jak pokażemy dalej, hooki pozwalają też na łączenie ich w nowy, niezwykle skuteczny sposób.
Jeżeli chcesz rozpocząć naukę o hookach, przejdź od razu do kolejnego rozdziału! Możesz też kontynuować lekturę tego, aby dowiedzieć się, dlaczego w ogóle dodaliśmy hooki, a także w jaki sposób będziemy je teraz stosować, bez potrzeby przepisywania naszych aplikacji.
Motywacja
Hooki rozwiązują wiele, pozornie niepowiązanych ze sobą, problemów Reacta, na które natknęliśmy się podczas ponad pięciu lat pisania i utrzymywania dziesiątek tysięcy komponentów. Nie ważne, czy dopiero uczysz się Reacta, używasz go na co dzień, czy nawet preferujesz inną bibliotekę (o podobnym, komponentowym modelu działania) - możliwe, że natknąłeś się na część tych problemów.
Współdzielenie logiki związanej ze stanem pomiędzy komponentami jest trudne
React nie oferuje sposobu na „dołączenie” powtarzalnego zachowania do komponentu (na przykład, połączenie go z magazynem (ang. store)). Jeżeli pracujesz z Reactem już jakiś czas, najprawdopodobniej znasz wzorce, takie jak właściwości renderujące (ang. render props) i komponenty wyższego rzędu (ang. higher-order components), które próbują rozwiązać ten problem. Wzorce te wymagają jednak modyfikacji komponentów w momencie ich użycia, co może być niewygodne i powodować, że kod jest trudniejszy w odbiorze. Jeśli spojrzysz na typową aplikację napisaną w Reakcie przy pomocy narzędzia React DevTools, najprawdopodobniej ujrzysz tam „piekło” komponentów opakowujących (ang. wrapper component), dostawców (ang. providers), konsumentów (ang. consumers), komponentów wyższego rzędu, właściwości renderujących i innych abstrakcji. Moglibyśmy, co prawda, filtrować je w DevToolsach, ale to tylko wskazuje na głębszy problem: React potrzebuje lepszego podstawowego mechanizmu do współdzielenia logiki związanej ze stanem.
Korzystając z hooków, możesz wydzielić logikę związaną ze stanem z komponentu. Dzięki czemu, nie wymaga on zależności przy testowaniu i jest łatwy w ponownym wykorzystaniu. Hooki pozwalają na ponowne użycie logiki związanej ze stanem, bez konieczności zmiany hierarchii komponentów. Sprawia to, że dzielenie się hookami pomiędzy wieloma komponentami lub ze społecznością jest proste.
Omówimy ten temat szerzej w rozdziale pt. „Tworzenie własnych hooków”.
Złożone komponenty stają się trudne do zrozumienia
Często musieliśmy utrzymywać komponenty, które z początku proste, urosły do rangi niemożliwego do utrzymania bałaganu logiki związanej ze stanem i efektów ubocznych (ang. side effects). Każda metoda cyklu życia zawiera zwykle mieszankę niepowiązanej ze sobą logiki. Na przykład, komponenty mogą pobierać dane w componentDidMount
i componentDidUpdate
. Jednakże metoda componentDidMount
może też zawierać logikę, która tworzy obserwatory zdarzeń (ang. event listeners). Następnie są one czyszczone w componentWillUnmount
. Wzajemnie powiązany kod zostaje podzielony pomiędzy różne metody, a z kolei zupełnie niezwiązany ze sobą kod trafia do jednej. Sprzyja to niekonsekwencji i popełnianiu błędów.
Wielokrotnie zdarza się, że nie ma możliwości rozbicia tych komponentów na mniejsze części, ponieważ logika związana ze stanem jest już wszędzie. Trudno jest też je testować. Jest to jeden z powodów, dla których wielu woli połączyć Reacta z zewnętrzną biblioteką do zarządzania stanem. To jednak często wprowadza zbyt wiele abstrakcji, zmusza do skakania pomiędzy plikami i utrudnia ponowne wykorzystanie komponentów.
Aby rozwiązać ten problem, hooki pozwalają podzielić komponent na mniejsze funkcje, bazując na powiązanych ze sobą częściach (takich jak tworzenie subskrypcji czy pobieranie danych), zamiast wymuszać sztuczny podział, związany z metodami cyklu życia. Ewentualnie, aby uczynić zachowanie komponentu bardziej przewidywalnym, możesz też dzięki hookom oddelegować zarządzanie lokalnym stanem komponentu do reduktora (ang. reducer).
Szerzej omówimy to w rozdziale Używanie hooka efektów.
Klasy dezorientują zarówno ludzi, jak i maszyny
Oprócz tego, że przez klasy trudniej jest ponownie wykorzystać i organizować kod, odkryliśmy, że mogą one również stanowić dużą przeszkodę w nauce Reacta. Trzeba przecież rozumieć, jak działa this
w JavaScripcie - a działa on tu zupełnie inaczej niż w większości języków programowania. Trzeba pamiętać o wiązaniu (ang. bind) funkcji obsługi zdarzeń (ang. event handler). Bez publicznych pól klas w ES2022 kod jest bardzo rozwlekły. Ludzie generalnie nie mają problemu ze zrozumieniem właściwości, stanu i kierunku przepływu danych z góry do dołu, a jednak klasy wciąż stanowią pewne wyzwanie. Wybór pomiędzy funkcyjnymi a klasowymi komponentami jest często przyczyną sporów, nawet pomiędzy doświadczonymi programistami Reacta.
Ponadto, React jest dostępny od około pięciu lat i chcielibyśmy mieć pewność, że pozostanie on tak samo istotny przez kolejne pięć. Jak pokazały biblioteki: Svelte, Angular, Glimmer i inne, kompilacja komponentów z wyprzedzeniem (ang. ahead-of-time) ma ogromny potencjał. Szczególnie jeśli nie jest ograniczona tylko do szablonów. Niedawno eksperymentowaliśmy z wyliczaniem wartości komponentów z użyciem Prepacka i wstępnie zaobserwowaliśmy obiecujące rezultaty. Odkryliśmy jednak, że komponenty klasowe mogą zachęcić do nieumyślnego stosowania pewnych wzorców, które spowodują spowolnienie tych optymalizacji. Klasy już teraz stanowią problem dla naszych narzędzi programistycznych. Na przykład, nie za dobrze się minifikują i często zawodzi przez nie „gorące przeładowanie” (ang. hot reloading). Chcemy przedstawić interfejs API, który zwiększy prawdopodobieństwo tego, że kod będzie optymalizowany.
Aby rozwiązać te problemy, Hooki pozwalają na korzystanie z większej liczby funkcjonalności Reacta, bez użycia klas. Koncepcyjnie, komponentom reactowym zawsze bliżej było do funkcji. Hooki zapewniają dostęp do funkcji bez poświęcania praktycznej natury Reacta. Hooki zapewniają dostęp do imperatywnych „furtek” i nie wymagają nauki skomplikowanych technik programowania funkcjonalnego lub reaktywnego.
Przykłady
Rozdział pt. „Hooki w pigułce” jest dobrym miejscem, by zacząć naukę o hookach
Strategia Stopniowego Wdrażania
TLDR: Nie ma planów na usunięcie klas z Reacta.
Zdajemy sobie sprawę, że programiści Reacta są skupieni na dostarczaniu produktów i nie mają czasu przyglądać się każdemu nowemu interfejsowi API, który jest wypuszczany. Hooki są wielką nowością i być może lepiej będzie zaczekać na więcej przykładów i poradników, zanim rozważysz ich naukę lub wdrożenie.
Rozumiemy też, że przy dodawaniu do Reacta nowego, podstawowego mechanizmu poprzeczka została postawiona niezwykle wysoko. Dla zainteresowanych przygotowaliśmy szczegółowy RFC, który dokładnie zgłębia nasze motywy oraz rzuca dodatkowe światło na konkretne decyzje projektowe i obecny stan techniki.
Co najważniejsze, hooki działają równolegle z istniejącym kodem, więc możesz wdrażać je stopniowo. Nie ma pośpiechu, aby migrować kod do hooków. Zalecamy unikanie „wielkiego przepisywania”, szczególnie dla istniejących, skomplikowanych komponentów klasowych. Potrzeba delikatnie przestawić myślenie, aby zacząć „myśleć hookami”. Z naszego doświadczenia wynika, że najlepiej poćwiczyć używanie hooków na nowych, niekrytycznych komponentach i upewnić się, że wszyscy członkowie zespołu czują się z nimi komfortowo. Po wypróbowaniu hooków, prosimy - prześlij nam opinię. Zarówno pozytywną, jak i negatywną.
Chcielibyśmy, żeby hooki objęły wszystkie możliwe przypadki użycia klas, ale w możliwej do przewidzenia przyszłości, będziemy kontynuować wsparcie komponentów klasowych. W Facebooku mamy dziesiątki tysięcy komponentów napisanych jako klasy i zdecydowanie nie planujemy ich przepisywania. Zamiast tego zaczynamy używać hooków w nowym kodzie, równolegle do klas.
Najczęściej zadawane pytania
Przygotowaliśmy rozdział pt. „Hooki - FAQ”, w którym odpowiedzieliśmy na najczęściej zadawane pytania.
Kolejne kroki
Po przeczytaniu tego rozdziału powinien ukształtować ci się obraz tego, jakie problemy rozwiązują hooki, ale wiele szczegółów jest jeszcze prawdopodobnie niejasnych. Nie martw się! Przejdźmy do następnego rozdziału, gdzie zaczniemy naukę o hookach na przykładach.