Dowiedz się, jak okiełznać użycie ReactaCallback Hook

Opublikowany: 2022-04-27

Nie jest tajemnicą, że React.js stał się w ostatnich latach bardzo popularny. Jest to obecnie biblioteka JavaScript wybierana przez wielu najwybitniejszych graczy w Internecie, w tym Facebooka i WhatsAppa.

Jednym z głównych powodów jego powstania było wprowadzenie hooków w wersji 16.8. Hooki Reacta pozwalają na korzystanie z funkcjonalności Reacta bez pisania komponentów klas. Teraz funkcjonalne komponenty z haczykami stały się strukturą docelową dla programistów do pracy z Reactem.

W tym poście na blogu zagłębimy się w jeden konkretny haczyk — useCallback — ponieważ dotyka on podstawowej części programowania funkcjonalnego znanej jako zapamiętywanie. Dowiesz się dokładnie, jak i kiedy wykorzystać hak useCallback i jak najlepiej wykorzystać jego możliwości zwiększające wydajność.

Gotowy? Zanurzmy się!


Co to jest zapamiętywanie?

Zapamiętywanie ma miejsce, gdy złożona funkcja przechowuje swoje dane wyjściowe, więc następnym razem, gdy zostanie wywołana z tym samym wejściem. Jest to podobne do buforowania, ale na poziomie bardziej lokalnym. Może pominąć wszelkie złożone obliczenia i szybciej zwrócić dane wyjściowe, ponieważ zostały już obliczone.

Może to mieć znaczący wpływ na alokację pamięci i wydajność, a to obciążenie ma złagodzić hak useCallback .

UseCallback vs useMemo w React

W tym miejscu warto wspomnieć, że useCallback ładnie łączy się z innym hakiem o nazwie useMemo . Omówimy je obie, ale w tym artykule skupimy się na useCallback jako głównym temacie.

Kluczową różnicą jest to, że useMemo zwraca zapamiętaną wartość, podczas gdy useCallback zwraca zapamiętaną funkcję. Oznacza to, że useMemo służy do przechowywania obliczonej wartości, podczas gdy useCallback zwraca funkcję, którą można później wywołać.

Te haki zwrócą wersję z pamięci podręcznej, chyba że zmieni się jedna z ich zależności (np. stan lub właściwości).

Przyjrzyjmy się dwóm funkcjom w akcji:

 import { useMemo, useCallback } from 'react' const values = [3, 9, 6, 4, 2, 1] // This will always return the same value, a sorted array. Once the values array changes then this will recompute. const memoizedValue = useMemo(() => values.sort(), [values]) // This will give me back a function that can be called later on. It will always return the same result unless the values array is modified. const memoizedFunction = useCallback(() => values.sort(), [values])

Powyższy fragment kodu jest wymyślonym przykładem, ale pokazuje różnicę między dwoma wywołaniami zwrotnymi:

  1. memoizedValue stanie się tablicą [1, 2, 3, 4, 6, 9] . Dopóki zmienna wartości pozostaje, memoizedValue i nigdy nie zostanie przeliczone.
  2. memoizedFunction będzie funkcją, która zwróci tablicę [1, 2, 3, 4, 6, 9] .

Wspaniałą rzeczą w tych dwóch wywołaniach zwrotnych jest to, że są one buforowane i zawieszają się, dopóki nie zmieni się tablica zależności. Oznacza to, że podczas renderowania nie będą zbierane śmieci.

React.js jest teraz biblioteką JavaScript wybieraną przez wielu największych graczy w Internecie, w tym Facebooka i WhatsAppa. Dowiedz się więcej w tym przewodniku ️ Kliknij, aby tweetować

Renderowanie i reagowanie

Dlaczego zapamiętywanie jest ważne w przypadku Reacta?

Ma to związek z tym, jak React renderuje twoje komponenty. React używa wirtualnego DOM przechowywanego w pamięci do porównywania danych i decydowania, co zaktualizować.

Wirtualny DOM pomaga React zwiększyć wydajność i zapewnia szybkość działania aplikacji. Domyślnie, jeśli jakakolwiek wartość w komponencie ulegnie zmianie, cały komponent zostanie ponownie wyrenderowany. To sprawia, że ​​React „reaguje” na dane wprowadzane przez użytkownika i umożliwia aktualizację ekranu bez ponownego ładowania strony.

Nie chcesz renderować swojego komponentu, ponieważ zmiany nie wpłyną na ten komponent. Tutaj przydaje się zapamiętywanie poprzez useCallback i useMemo .

Kiedy React ponownie renderuje twój komponent, odtwarza również funkcje, które zadeklarowałeś w swoim komponencie.

Zauważ, że porównując równość funkcji z inną funkcją, zawsze będą one fałszywe. Ponieważ funkcja jest również obiektem, będzie się równać tylko sobie:

 // these variables contain the exact same function but they are not equal const hello = () => console.log('Hello Matt') const hello2 = () => console.log('Hello Matt') hello === hello2 // false hello === hello // true

Innymi słowy, kiedy React ponownie renderuje twój komponent, zobaczy wszystkie funkcje, które są zadeklarowane w twoim komponencie jako nowe funkcje.

W większości przypadków jest to w porządku, a proste funkcje są łatwe do obliczenia i nie wpływają na wydajność. Ale w innych przypadkach, gdy nie chcesz, aby funkcja była postrzegana jako nowa funkcja, możesz liczyć na pomoc useCallback .

Możesz pomyśleć: „Kiedy nie chciałbym, aby funkcja była postrzegana jako nowa funkcja?” Cóż, są pewne przypadki, w których useCallback ma więcej sensu:

  1. Przekazujesz funkcję do innego komponentu, który jest również zapamiętywany ( useMemo )
  2. Twoja funkcja ma stan wewnętrzny, który musi zapamiętać
  3. Twoja funkcja jest zależnością innego hooka, na przykład useEffect

Wydajność Korzyści płynące z użycia ReactOddzwoń

Gdy useCallback jest odpowiednio używany, może przyspieszyć działanie aplikacji i zapobiec ponownemu renderowaniu komponentów, jeśli nie są potrzebne.

Załóżmy na przykład, że masz komponent, który pobiera dużą ilość danych i jest odpowiedzialny za wyświetlanie tych danych w formie wykresu lub wykresu, w następujący sposób:

Kolorowy wykres słupkowy porównujący całkowity czas transakcji PHP, MySQL, Reddis i zewnętrznych (innych) w milisekundach.
Wykres słupkowy wygenerowany przy użyciu komponentu React.

Załóżmy, że komponent nadrzędny komponentu wizualizacji danych jest ponownie renderowany, ale zmienione właściwości lub stan nie mają wpływu na ten komponent. W takim przypadku prawdopodobnie nie chcesz ani nie musisz ponownie renderować i pobierać wszystkich danych. Unikanie tego ponownego renderowania i ponownego pobierania może zaoszczędzić przepustowość użytkownika i zapewnić płynniejszą obsługę.

Masz problemy z przestojami i WordPressem? Kinsta to rozwiązanie hostingowe zaprojektowane, aby zaoszczędzić Twój czas! Sprawdź nasze funkcje

Wady React useCallback

Chociaż ten hak może pomóc Ci poprawić wydajność, ma również swoje pułapki. Kilka rzeczy do rozważenia przed użyciem useCallback (i useMemo ) to:

  • Zbieranie śmieci: inne funkcje, które nie zostały jeszcze zapamiętane, zostaną odrzucone przez React, aby zwolnić pamięć.
  • Alokacja pamięci: Podobnie jak w przypadku wyrzucania śmieci, im więcej posiadasz zapamiętanych funkcji, tym więcej pamięci będzie wymagane. Dodatkowo, za każdym razem, gdy używasz tych wywołań zwrotnych, w React znajduje się garść kodu, który potrzebuje jeszcze więcej pamięci, aby zapewnić buforowane dane wyjściowe.
  • Złożoność kodu: Kiedy zaczynasz zawijać funkcje w te zaczepy, natychmiast zwiększasz złożoność kodu. Wymaga to teraz lepszego zrozumienia, dlaczego te haki są używane, i potwierdzenia, że ​​są używane poprawnie.

Świadomość powyższych pułapek może oszczędzić ci bólu głowy związanego z samotnym natknięciem się na nie. Rozważając użycie useCallback , upewnij się, że korzyści związane z wydajnością przeważą nad wadami.

React useCallback Przykład

Poniżej znajduje się prosta konfiguracja z komponentem Button i komponentem Counter. Licznik ma dwie części stanu i renderuje dwa składniki Button, z których każdy zaktualizuje oddzielną część stanu składników licznika.

Komponent Button ma dwie właściwości: handleClick i name. Za każdym razem, gdy Button jest renderowany, loguje się do konsoli.

 import { useCallback, useState } from 'react' const Button = ({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> } const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) return ( <> {countOne} {countTwo} <Button handleClick={() => setCountOne(countOne + 1)} name="button1" /> <Button handleClick={() => setCountTwo(countTwo + 1)} name="button1" /> </> ) }

W tym przykładzie za każdym razem, gdy klikniesz dowolny przycisk, zobaczysz to w konsoli:

 // counter rendered // button1 rendered // button2 rendered

Teraz, jeśli zastosujemy useCallback do naszych funkcji handleClick i zawiniemy nasz Button w React.memo , możemy zobaczyć, co zapewnia nam useCallback . React.memo jest podobny do useMemo i pozwala nam zapamiętywać komponent.

 import { useCallback, useState } from 'react' const Button = React.memo(({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> }) const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne) const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo]) return ( <> {countOne} {countTwo} <Button handleClick={memoizedSetCountOne} name="button1" /> <Button handleClick={memoizedSetCountTwo} name="button1" /> </> ) }

Teraz, gdy klikniemy jeden z przycisków, zobaczymy tylko przycisk, który kliknęliśmy, aby zalogować się do konsoli:

 // counter rendered // button1 rendered // counter rendered // button2 rendered

Zastosowaliśmy memoization do naszego komponentu przycisku, a wartości właściwości, które są do niego przekazywane, są postrzegane jako równe. Dwie funkcje handleClick są buforowane i będą postrzegane jako ta sama funkcja przez React do momentu zmiany wartości elementu w tablicy zależności (np. countOne , countTwo ).

Chcesz dowiedzieć się dokładnie, jak i kiedy korzystać z haka useCallback, a także jak najlepiej wykorzystać jego możliwości zwiększające wydajność? Zacznij tutaj! Kliknij, aby tweetować

Streszczenie

useCallback i useMemo są fajne, pamiętaj, że mają one specyficzne przypadki użycia — nie powinieneś zawijać każdej funkcji w te hooki. Jeśli funkcja jest złożona obliczeniowo, zależność innego hooka lub właściwości przekazanej do zapamiętanego komponentu jest dobrym wskaźnikiem, do którego warto sięgnąć po useCallback .

Mamy nadzieję, że ten artykuł pomógł ci zrozumieć tę zaawansowaną funkcjonalność Reacta i pomógł ci zyskać więcej pewności w programowaniu funkcjonalnym!