Demistyfikujące hooki React — useCallback

Dec 03 2022
W tym artykule zbadamy, kiedy i jak używać haka useCallback Reacta oraz błąd popełniany przez większość młodszych programistów.
Rozpoczęcie Jeśli chcesz podążać za lokalnym środowiskiem IDE, tutaj znajdziesz repozytorium GitHub. Równość referencyjna jest fundamentalną koncepcją zarówno w JavaScript, jak iw informatyce jako całości.

Rozpoczęcie pracy

Jeśli chcesz podążać za lokalnym środowiskiem IDE, możesz znaleźć repozytorium GitHub tutaj .

  • clone
  • cd client
  • npm i
  • npm start

Równość referencyjna jest fundamentalną koncepcją zarówno w JavaScript, jak iw informatyce jako całości. Zacznijmy więc od zademonstrowania tego w działaniu.

Osadziłem zrzuty ekranu w całym artykule, aby ułatwić korzystanie z urządzenia mobilnego. Jeśli jesteś na komputerze i sklonowałeś go, uruchom, referentialEquality.jsaby obserwować dane wyjściowe lub po prostu pobaw się fragmentem kodu JSFiddle osadzonym poniżej.

Podczas oceniania, czy integer 1jest ściśle równe integer 1, konsola drukuje true. Dzieje się tak, ponieważ, no cóż… the integer 1 jest ściśle równe integer 1.

liczby całkowite są ściśle równe

Widzimy ten sam wynik podczas oceny dwóch łańcuchów.

ciągi są ściśle równe

Oczywiście będzie tak zawsze w przypadku dwóch pierwotnych typów danych o tej samej wartości.

A co ze strukturami danych? Na przykład dwa literały obiektowe z tymi samymi parami klucz/wartość? A co z pustymi literałami obiektowymi?

obiekty nie są ściśle równe

Dlaczego ten wydruk false? Porównując, czy te dwa literały obiektowe są ściśle równe, JavaScript używa ich odpowiednich adresów pamięci .

Innymi słowy, te dwa obiekty mogą zawierać te same wartości , ale nie odwołują się do tego samego obiektu . Wyglądają tak samo, ale zajmują dwa różne miejsca w pamięci .

To samo dotyczy tego, czy porównujesz dwa literały obiektowe, dwa literały tablicowe czy dwie funkcje!

tablice nie są ściśle równe

Aby to dokładniej zademonstrować, zdefiniujemy funkcję func, która zwraca anonimową funkcję, która z kolei zwraca coś innego ( np. element JSX ).

tworząc udawany komponent funkcjonalny

Następnie przypiszemy dwie różne funkcje firstRenderi secondRender, równe wartości zwróconej przez func.

przypisanie naszych definicji funkcji

Pomyśl o funcswoim komponencie funkcjonalnym React, podczas gdy firstRenderjest funkcją wewnątrz niego na pierwszym renderowaniu i secondRenderjest funkcją wewnątrz niego na drugim renderowaniu.

Mimo że firstRenderi secondRenderzwracają tę samą wartość i są przypisane z tej samej definicji, nie mają równości referencyjnej . W rezultacie za każdym razem, gdy komponent jest renderowany, ponownie definiuje tę funkcję.

nasze dwie funkcje nie są ściśle równe

Niestety, w JavaScript nie jest łatwo wydrukować te adresy pamięci, tak jak w Pythonie, ale nieco bardziej dogłębne wyjaśnienie stosunku referencji do wartości można znaleźć w tym artykule z freeCodeCamp.

Ten temat może stać się gęsty i nie musisz dziś prowadzić zajęć na ten temat. Więc na razie pamiętaj:

  • prymitywny typ danych ===prymitywny typ danych
  • struktura danych !==struktura danych.

Kod startowy

Po uruchomieniu naszej aplikacji otwórz BookDetails.jsxkomponent i ponownie zapisz. Pierwszą rzeczą, którą możemy zauważyć na naszym serwerze deweloperskim React, jest to, WARNINGco często jest ignorowane przez młodych programistów. Kiedy trafisz na siłę roboczą i zaczniesz pisać kod do produkcji, twoje lintery będą jeszcze bardziej rygorystyczne niż to, co jest wbudowane w create-react-app. WARNINGSzmieni się na ERRORS, a niektóre linters nie pozwolą ci nacisnąć, zanim zajmiesz się tymi ERRORS.

Więc zamiast go ignorować, zastanówmy się, jak go leczyć.

Proponowane rozwiązania React

UWAGA: może być konieczne ponowne zapisanie, BookDetails.jsxaby to utworzyćWARNING

Jeśli zagłębimy się w React Docs , możemy rozszyfrować na wpół mylące proponowane rozwiązania tego problemu WARNINGw następujący sposób:

1. Dołącz definicję funkcji do pliku useEffect.

  • Nie możemy wywołać tej funkcji gdzie indziej, chyba że ponownie ją zdefiniujemy.
  • Spowoduje to wyzwolenie useEffect każdej zmiany stanu lub rekwizytów, czasami powodując nieskończone ponowne renderowanie. A w naszym przypadku, ponieważ wywołujemy naszą funkcję ustawiającą stan po wywołaniu interfejsu API, może to spowodować przeciążenie naszego interfejsu API nieskończoną liczbą żądań punktów końcowych.
  • Funkcja nie zostanie wywołana.
  • Gdy komponent renderuje się po raz pierwszy, zdefiniuje naszą funkcję, która uruchomi useEffect, co spowoduje ponowne wyrenderowanie komponentu, co ponownie zdefiniuje funkcję, która uruchomi , useEffectco spowoduje ponowne wyrenderowanie komponentu, co ponownie zdefiniuje funkcję…

Najprostszym i preferowanym rozwiązaniem byłoby „dołączenie go”, czyli przeniesienie getBookDetailsdefinicji funkcji do pliku useEffect. Jest to zgodne z zasadą programowania obiektowego znaną jako enkapsulacja .

Ale powiedzmy, że wiemy, że musimy wywołać tę funkcję gdzie indziej. Czy powinniśmy to przedefiniować później? To nie jest bardzo SUCHE z naszej strony.

Zmieńmy naszą tablicę zależności, aby zawierała nasze odwołanie do funkcji. Twój useEffectpowinien teraz wyglądać tak.

w tym naszą funkcję w tablicy zależności

I pozostaje getBookDetailszdefiniowany powyżej .useEffect

oryginalna definicja naszej funkcji

Teraz mamy noweWARNING

zdefiniowanie naszej funkcji poza useEffect przy uwzględnieniu jej w tablicy zależności spowoduje nieskończone ponowne renderowanie

Wprowadź hak useCallback

Krótko mówiąc, useCallbackhak umożliwia buforowanie lub „zapamiętywanie” funkcji między ponownymi renderowaniami komponentu. Wykonuje podobne zadanie do useMemo, którego niuanse omówimy w innym artykule.

Jeśli interesują Cię szczegółowe informacje na ten temat, możesz przeczytać więcej w dokumentacji React .

Zwróć uwagę na ich ostrzeżenie:

Powinieneś polegać tylko useCallbackna optymalizacji wydajności. Jeśli Twój kod nie działa bez niego, znajdź podstawowy problem i napraw go najpierw. Następnie możesz dodać useCallback, aby poprawić wydajność.

useCallbackSkładnia

useCallbackSkładnia 's jest bardzo podobna do useEffectskładni '', którą już znamy. Spójrz na szkielety każdego z nich.

oba haki mają identyczne szkielety składniowe

Niewielka różnica polega na tym useEffect, że w przypadku , mówimy funkcji anonimowej, aby wykonała naszą funkcję, podczas gdy w przypadku useCallback, przypisujemy wartość zwracaną do odwołania, które ma zostać wywołane gdzie indziej.

Za pomocą useCallback

Najpierw zaimportujemy useCallbackz 'react'. Zamiast dodawać nowy wiersz, najlepiej zdekonstruować go wraz z innymi naszymi importami.

elementy zdestrukturyzowane z tego samego opakowania najlepiej importować w tej samej linii

Teraz możemy przypisać getBookDetailswartość zwróconą z useCallbackwywołania funkcji.

przypisz zwrot useCallback do naszej referencji

Następnie dodajemy całą składnię dla useCallback. Zapamiętaj swoją tablicę zależności!

wypełnij szkielet useCallback

W naszym przykładzie potrzebujemy asyncprzed naszymi parametrami.

dodaj asynchroniczne

Na koniec dodajemy logikę naszej funkcji do bloku kodu.

dodaj naszą logikę do bloku kodu useCallback

Gdy zapiszemy, dostajemy… kolejną WARNING.

Zwrot useCallback zależy od wartości id

Dlaczego nasza tablica zależności powinna śledzić idzmienną?

Przemyślmy to.

  • Jeśli wartość idzmian getBookDetailsmusi trafić w inny punkt końcowy, więc React powinien go przedefiniować. Definicja getBookDetailsdosłownie zależy od wartości id.
gotowe wersje obu haków

I wreszcie, to jest to! Widzimy kolor zielony na naszym serwerze deweloperskim React. Szczęśliwy Linter to szczęśliwy Senior Developer. A szczęśliwy Senior Developer to szczęśliwy Ty!

Zawsze szukam nowych przyjaciół i kolegów. Jeśli ten artykuł okazał się pomocny i chcesz się połączyć, możesz mnie znaleźć w dowolnym z moich domów w sieci.

GitHub | Twitter | LinkedIn | Stronie internetowej

Zasoby

  • Równość referencyjna
  • Pierwotne typy danych JavaScript a struktury danych
  • Kapsułkowanie
  • useCallback
  • Reaguj Dokumenty