Najlepsze praktyki z hakami React
Opublikowany: 2022-03-10 React Hooki to nowy dodatek w React 16.8, który pozwala używać stanu i innych funkcji Reacta bez pisania komponentu class
. Innymi słowy, hooki to funkcje, które pozwalają „zahaczyć się” o funkcje stanu React i cyklu życia z komponentów funkcyjnych. (Nie działają wewnątrz komponentów class
.)
React udostępnia kilka wbudowanych hooków, takich jak useState
. Możesz także stworzyć własne hooki, aby ponownie wykorzystać zachowanie stanowe pomiędzy różnymi komponentami. Poniższy przykład pokazuje licznik, którego stan jest zarządzany za pomocą useState()
. Za każdym razem, gdy klikniesz na przycisk, używamy setCount()
do aktualizacji wartości count
o 1
.
Ten przykład renderuje licznik o wartości 0
. Kliknięcie przycisku powoduje zwiększenie wartości o 1
. Wartość początkowa składnika jest definiowana za pomocą useState
.
const [count, setCount] = useState(0)
Jak widać, ustawiliśmy to na 0
. Następnie używamy metody onClick()
, aby wywołać setCount
, gdy chcemy zwiększyć wartość.
<button onClick={() => setCount(count + 1)}> Click me </button>
Przed wydaniem haków reakcji w tym przykładzie używano by więcej linii kodu, ponieważ musielibyśmy użyć komponentu class
.
Zasady haków reakcyjnych
Zanim zagłębimy się w najlepsze praktyki, musimy zrozumieć zasady React Hooks, które są również jednymi z podstawowych koncepcji praktyk przedstawionych w tym artykule.
Hooki React są funkcjami JavaScript, ale podczas ich używania musisz przestrzegać dwóch zasad.
- Wywołaj hooki na najwyższym poziomie;
- Wywołuj hooki tylko z komponentów React.
Uwaga : Te dwie reguły zostały wprowadzone w hookach reakcyjnych, w przeciwieństwie do bycia częścią samego JavaScriptu.
Przyjrzyjmy się tym zasadom bardziej szczegółowo.
Zadzwoń Hooki na najwyższym poziomie
Nie wywołuj hooków wewnątrz pętli, warunków lub funkcji zagnieżdżonych. Zawsze używaj hooków na najwyższym poziomie funkcji React. Przestrzegając tej zasady, zapewniasz, że hooki są wywoływane w tej samej kolejności za każdym razem, gdy renderowany jest komponent. To właśnie pozwala Reactowi na poprawne zachowanie stanu hooków między wielokrotnymi useState
i useEffect
.
Stwórzmy składnik Form
, który będzie miał dwa stany:
-
accountName
-
accountDetail
Te stany będą miały wartości domyślne, użyjemy zaczepu useEffect
, aby zachować stan w pamięci lokalnej naszej przeglądarki lub w tytule naszego dokumentu.
Teraz ten składnik może pomyślnie zarządzać swoim stanem, jeśli pozostanie taki sam między wieloma wywołaniami useState
i useEffect
.
function Form() { // 1. Use the accountName state variable const [accountName, setAccountName] = useState('David'); // 2. Use an effect for persisting the form useEffect(function persistForm() { localStorage.setItem('formData', accountName); }); // 3. Use the accountDetail state variable const [accountDetail, setAccountDetail] = useState('Active'); // 4. Use an effect for updating the title useEffect(function updateStatus() { document.title = accountName + ' ' + accountDetail; }); // ... }
Jeśli zmieni się kolejność naszych hooków (co może być możliwe, gdy zostaną wywołane w pętlach lub warunkach warunkowych), Reactowi trudno będzie wymyślić, jak zachować stan naszego komponentu.
// ------------ useState('David') // 1. Initialize the accountName state variable with 'David' useEffect(persistForm) // 2. Add an effect for persisting the form useState('Active') // 3. Initialize the accountdetail state variable with 'Active' useEffect(updateStatus) // 4. Add an effect for updating the status // ------------- // Second render // ------------- useState('David') // 1. Read the accountName state variable (argument is ignored) useEffect(persistForm) // 2. Replace the effect for persisting the form useState('Active') // 3. Read the accountDetail state variable (argument is ignored) useEffect(updateStatus) // 4. Replace the effect for updating the status // ...
To jest kolejność, w jakiej React nazywa nasze hooki. Ponieważ kolejność pozostaje taka sama, będzie w stanie zachować stan naszego komponentu. Ale co się stanie, jeśli umieścimy wywołanie Hook w warunku?
// We're breaking the first rule by using a Hook in a condition if (accountName !== '') { useEffect(function persistForm() { localStorage.setItem('formData', accountName); }); }
accountName !== ''
jest true
przy pierwszym renderowaniu, więc uruchamiamy ten hook. Jednak przy następnym renderowaniu użytkownik może wyczyścić formularz, czyniąc warunek false
. Teraz, gdy pomijamy ten hook podczas renderowania, kolejność wywołań hooka staje się inna:
useState('David') // 1. Read the accountName state variable (argument is ignored) // useEffect(persistForm) // This Hook was skipped! useState('Active') // 2 (but was 3). Fail to read the accountDetails state variable useEffect(updateStatus) // 3 (but was 4). Fail to replace the effect
React nie wiedziałby, co zwrócić przy drugim wywołaniu funkcji useState
Hook. React spodziewał się, że drugie wywołanie hooka w tym komponencie odpowiada efektowi persistForm
, tak jak podczas poprzedniego renderowania — ale już tak nie jest. Od tego momentu każde następne wywołanie Hook
po tym, które pominęliśmy, również przesunęłoby się o jeden — prowadząc do błędów.
Dlatego hooki muszą być wywoływane na najwyższym poziomie naszych komponentów. Jeśli chcemy uruchomić efekt warunkowo, możemy umieścić ten warunek w naszym hooku.
Uwaga : zapoznaj się z dokumentacją React Hook, aby przeczytać więcej na ten temat.
Wywołuj hooki tylko z komponentów React
Nie wywołuj hooków ze zwykłych funkcji JavaScript. Zamiast tego możesz wywoływać hooki z komponentów funkcyjnych React. Przyjrzyjmy się różnicy między funkcją JavaScript a komponentem React poniżej:
Funkcja JavaScript
import { useState } = "react"; function toCelsius(fahrenheit) { const [name, setName] = useState("David"); return (5/9) * (fahrenheit-32); } document.getElementById("demo").innerHTML = toCelsius;
Tutaj importujemy hook useState
z pakietu React, a następnie deklarujemy naszą funkcję. Ale jest to niepoprawne, ponieważ nie jest to komponent React.
Funkcja reakcji
import React, { useState} from "react"; import ReactDOM from "react-dom"; function Account(props) { const [name, setName] = useState("David"); return <p>Hello, {name}! The price is <b>{props.total}</b> and the total amount is <b>{props.amount}</b></p> } ReactDom.render( <Account total={20} amount={5000} />, document.getElementById('root') );
Mimo że korpus obu wygląda podobnie, ten drugi staje się komponentem, gdy importujemy Reacta do pliku. To właśnie umożliwia nam używanie w środku takich rzeczy jak hooki JSX i React.
Jeśli zdarzyło Ci się zaimportować preferowany hook bez importowania Reacta (co czyni go zwykłą funkcją), nie będziesz mógł użyć zaimportowanego hooka, ponieważ hook jest dostępny tylko w komponencie React.
Wywołaj hooki z niestandardowych hooków
Niestandardowy hook to funkcja JavaScript, której nazwa zaczyna się od use
i która może wywoływać inne hooki. Na przykład useUserName
jest używane poniżej niestandardowego hooka, który wywołuje useState
i useEffect
. Pobiera dane z API, przechodzi przez dane w pętli i wywołuje setIsPresent()
, jeśli określona nazwa użytkownika, którą otrzymał, jest obecna w danych API.
export default function useUserName(userName) { const [isPresent, setIsPresent] = useState(false); useEffect(() => { const data = MockedApi.fetchData(); data.then((res) => { res.forEach((e) => { if (e.name === userName) { setIsPresent(true); } }); }); }); return isPresent; }
Możemy następnie ponownie wykorzystać funkcjonalność tego haka w innych miejscach, w których potrzebujemy takich w naszej aplikacji. W takich miejscach, poza potrzebami, nie musimy już wywoływać useState
lub useEffect
.
Przestrzegając tej zasady, zapewniasz, że cała logika stanowa w komponencie jest wyraźnie widoczna w jego kodzie źródłowym.
Wtyczka ESLint
Wtyczka ESLint o nazwie eslint-plugin-react-hooks
wymusza powyższe zasady. Przydaje się to w egzekwowaniu zasad podczas pracy nad projektem. Proponuję skorzystać z tej wtyczki podczas pracy nad swoim projektem, zwłaszcza podczas pracy z innymi. Możesz dodać tę wtyczkę do swojego projektu, jeśli chcesz ją wypróbować:
// Your ESLint configuration { "plugins": [ // ... "react-hooks" ], "rules": { // ... "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies } }
Ta wtyczka jest domyślnie dołączona do aplikacji Create React. Nie musisz więc go dodawać, jeśli uruchamiasz swoje aplikacje React za pomocą aplikacji Create-React-App.
Myślenie w hakach
Przyjrzyjmy się pokrótce komponentom class
i komponentom funkcjonalnym (z hookami), zanim przejdziemy do kilku najlepszych praktyk dotyczących hooków.
Najprostszym sposobem zdefiniowania komponentu w React jest napisanie funkcji JavaScript, która zwraca element React:
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
Komponent Welcome
akceptuje props
, czyli obiekt zawierający dane i zwracający element React. Następnie możemy zaimportować i wyrenderować ten komponent w innym komponencie.
Komponent class
wykorzystuje metodologię programowania o nazwie Encapsulation , co w zasadzie oznacza, że wszystko, co dotyczy komponentu klasy, będzie w nim mieszkać. Metody cyklu życia ( constructors
, componentDidMount()
, render
, itd.) nadają komponentom przewidywalną strukturę.
Enkapsulacja jest jedną z podstaw programowania obiektowego (ang. O bject-Oriented P rograming ). Odnosi się do grupowania danych w ramach metod operujących na tych danych i służy do ukrywania wartości lub stanu obiektu danych strukturalnych wewnątrz klasy — zapobiegając bezpośredniemu dostępowi do nich nieupoważnionych stron.
W przypadku hooków skład komponentu zmienia się z kombinacji hooków cyklu życia — w funkcje z pewnym renderowaniem na końcu.
Komponent funkcji
Poniższy przykład pokazuje, jak niestandardowe hooki mogą być używane w komponencie funkcjonalnym (bez pokazywania treści). Jednak to, co robi lub może zrobić, nie jest ograniczone. Może to być tworzenie instancji zmiennych stanu, konsumowanie kontekstów, subskrybowanie komponentu z różnymi efektami ubocznymi — lub wszystkie powyższe, jeśli używasz niestandardowego zaczepu!
function { useHook{...}; useHook{...}; useHook{...}; return (
...); }
Składnik klasy
Komponent class
wymaga rozszerzenia z React.Component
i utworzenia funkcji render
, która zwraca element React. To wymaga więcej kodu, ale daje też pewne korzyści.
class { constructor(props) {...} componentDidMount() {...} componentWillUnmount() {...} render() {...} }
Korzystanie z funkcjonalnych komponentów w React daje pewne korzyści:
- Łatwiej będzie oddzielić komponenty kontenerowe i prezentacyjne, ponieważ musisz więcej pomyśleć o stanie swojego komponentu, jeśli nie masz dostępu do
setState()
w swoim komponencie. - Komponenty funkcjonalne są znacznie łatwiejsze do odczytania i przetestowania , ponieważ są zwykłymi funkcjami JavaScript bez stanów lub haczyków cyklu życia.
- W efekcie otrzymujesz mniej kodu.
- Zespół React wspomniał, że w przyszłych wersjach Reacta może nastąpić wzrost wydajności komponentów funkcjonalnych.
Prowadzi to do pierwszej najlepszej praktyki podczas korzystania z hooków reakcji.
Najlepsze praktyki dotyczące haków
1. Uprość swoje hooki
Prostota React Hooks da Ci możliwość efektywnego kontrolowania i manipulowania tym, co dzieje się w komponencie przez cały okres jego eksploatacji. Unikaj pisania własnych hooków tak często, jak to tylko możliwe ; możesz useState()
lub useEffect()
zamiast tworzyć własne podpięcie.
Jeśli zauważysz, że korzystasz z wielu niestandardowych hooków, które są powiązane pod względem funkcjonalności, możesz stworzyć niestandardowy hook, który będzie dla nich opakowaniem. Przyjrzyjmy się dwóm różnym elementom funkcjonalnym z haczykami poniżej.
Komponent funkcjonalny v1
function { useHook(...); useHook(...); useHook(...); return( <div>...</div> ); }
Komponent funkcjonalny v2
function { useCustomHook(...); useHook(...); useHook(...); return( <div>...</div> ); }
v2 jest lepszą wersją, ponieważ sprawia, że hook jest prosty, a wszystkie inne useHook
są odpowiednio wbudowane. Pozwala nam to tworzyć funkcje, które można ponownie wykorzystać w różnych komponentach, a także daje nam więcej możliwości efektywnej kontroli i manipulowania naszymi komponentami. Zamiast stosować v1, w której nasze komponenty są zaśmiecone hookami, powinieneś użyć wersji v2, która ułatwi debugowanie, a twój kod będzie czystszy.
2. Zorganizuj i ustrukturyzuj swoje haczyki
Jedną z zalet React Hooks jest możliwość pisania mniej kodu, który jest łatwy do odczytania. W niektórych przypadkach ilość funkcji useEffect()
i useState()
może być nadal myląca. Jeśli utrzymasz swój komponent w porządku, pomoże to w czytelności i zapewni spójny i przewidywalny przepływ komponentów. Jeśli twoje niestandardowe hooki są zbyt skomplikowane, zawsze możesz podzielić je na podrzędne hooki. Wyodrębnij logikę swojego komponentu do niestandardowych hooków, aby twój kod był czytelny.
3. Użyj fragmentów kodu React Hooks
Fragmenty haków reakcji to rozszerzenie Visual Studio Code, które ułatwia i przyspiesza hakowanie reakcji. Obecnie obsługiwanych jest pięć hooków:
-
useState()
-
useEffect()
-
useContext()
-
useCallback()
-
useMemo()
Dodano również inne fragmenty. Próbowałem pracować z tymi hookami i była to jedna z najlepszych praktyk, których osobiście używałem podczas pracy z nimi.
Istnieją dwa sposoby dodawania fragmentów kodu React Hooks do projektu:
- Komenda
Uruchom VS Code Quick open ( Ctrl + P ), wklejext install ALDuncanson.react-hooks-snippets
i naciśnij Enter . - Rynek rozszerzeń
Uruchom „VS Code Extension Marketplace” ( Ctrl + Shift + X ) i wyszukaj „React Hook Snippets”. Następnie poszukaj ikony „Alduncanson”.
Polecam pierwszy fragment. Przeczytaj więcej o fragmentach tutaj lub sprawdź najnowsze fragmenty hooków tutaj.
4. Uwzględnij zasady hooków
Staraj się zawsze uwzględniać dwie zasady hooków, których nauczyliśmy się wcześniej, podczas pracy z hookami reakcyjnymi.
- Wywołuj swoje hooki tylko na najwyższym poziomie. Nie wywołuj hooków wewnątrz pętli, warunków lub funkcji zagnieżdżonych.
- Zawsze wywołuj hooki z komponentów funkcji React lub z własnych hooków, nie wywołuj hooków ze zwykłych funkcji JavaScript.
Wtyczka ESlint o nazwie eslint-plugin-react-hooks
wymusza te dwie zasady, możesz dodać tę wtyczkę do swojego projektu, jeśli chcesz, jak wyjaśniliśmy powyżej w sekcji reguł hooków.
Najlepsze praktyki nie zostały w pełni rozwiązane, ponieważ hooki są wciąż stosunkowo nowe. Tak więc adopcja powinna być podjęta z ostrożnością, którą można by podjąć przy adopcji w każdej wczesnej technologii. Mając to na uwadze, hooki są drogą do przyszłości Reacta.
Wniosek
Mam nadzieję, że podobał Ci się ten samouczek. Poznaliśmy dwie najważniejsze zasady hooków reakcyjnych i jak efektywnie myśleć w hookach. Przyjrzeliśmy się komponentom funkcjonalnym i niektórym najlepszym praktykom w pisaniu hooków we właściwy i skuteczny sposób. Choć zasady są krótkie, ważne jest, aby były one Twoim kompasem przewodnim podczas pisania zasad. Jeśli masz skłonność do zapomnienia, możesz skorzystać z wtyczki ESLint, aby to wymusić.
Mam nadzieję, że wyciągniesz wszystkie wnioski z lekcji w swoim następnym projekcie React. Powodzenia!
Zasoby
- „Przedstawiamy hooki”, React Docs
- „Funkcjonalne a komponenty klasowe w reakcji”, David Joch, Medium
- „Miksyny uważane za szkodliwe”, Dan Abramov, blog React
- „React Hooks: najlepsze praktyki i zmiana sposobu myślenia”, Bryan Manuele, Medium
- „React Hooks Snippets For VS Code”, Anthony Davis, Visual Code Marketplace