Hooki - interfejs API

Hooki są nowym dodatkiem w Reakcie 16.8. Pozwalają one używać stanu i innych funkcjonalności Reacta, bez użycia klas.

Ten rozdział opisuje interfejs API hooków wbudowanych w Reacta.

Jeżeli pierwszy raz stykasz się z hookami, możesz najpierw sprawdzić rozdział pt. „Hooki w pigułce”. W rozdziale z najczęściej zadawanymi pytaniami możesz znaleźć inne przydatne informacje.

Podstawowe hooki

useState

const [state, setState] = useState(initialState);

Zwraca zmienną przechowującą lokalny stan i funkcję do jego aktualizacji.

Podczas pierwszego renderowania zwrócona wartość stanu (state) jest taka sama, jak wartość przekazana jako pierwszy argument (initialState).

Funkcja setState jest używana do aktualizacji stanu. Przyjmuje ona nową wartość stanu i kolejkuje ponowne renderowanie komponentu.

setState(newState);

Podczas kolejnych ponownych renderowań pierwszą wartością zwracaną przez useState będzie zawsze najnowszy, zaktualizowany stan.

Uwaga

React daje gwarancje, że funkcja setState jest tożsamościowa i że nie zmienia się podczas kolejnych renderowań. Dlatego też można ją bezpiecznie pominąć na liście zależności useEffect i useCallback.

Aktualizacje funkcyjne

Jeżeli nowy stan wyliczany jest z poprzedniego, możesz przekazać funkcję jako argument do setState. Funkcja otrzymuje poprzednią wartość stanu, a zwraca nową, zaktualizowaną wartość. Oto przykład komponentu licznika, który wykorzystuje obie formy setState:

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Licznik: {count}
      <button onClick={() => setCount(initialCount)}>Zresetuj</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
    </>
  );
}

Przyciski „+” i „-” wykorzystują formę funkcyjną, ponieważ zaktualizowana wartość bazuje na poprzedniej. Z kolei przycisk „Zresetuj” używa normalnej formy, ponieważ zawsze przywraca początkową wartość.

Uwaga

W przeciwieństwie do metody setState znanej z komponentów klasowych, funkcja useState nie scala automatycznie obiektów reprezentujących aktualizację. Możesz powielić to zachowanie, łącząc formę aktualizacji funkcyjnej ze składnią operatora rozszczepienia (ang. spread operator) obiektu:

setState(prevState => {
  // Object.assign również zadziała
  return {...prevState, ...updatedValues};
});

Inną opcją jest hook useReducer, który jest bardziej odpowiedni do zarządzania obiektami stanów, zawierającymi wiele pod-wartości.

Leniwa inicjalizacja stanu

Argument initialState jest wartością stanu używaną podczas pierwszego rendera. W kolejnych renderowaniach jest on pomijany. Jeśli początkowy stan jest wynikiem kosztownych obliczeń, możesz zamiast tego przekazać funkcję, która zostanie wykonana tylko przy pierwszym renderowaniu:

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

Wycofanie się z aktualizacji stanu

Jeżeli zaktualizujesz hook stanu do takiej samej wartości, jaka jest aktualnie przechowywana w stanie, React „wymiga się”, nie aktualizując potomków i nie uruchamiając efektów. (React używa algorytmu porównywania Object.is.)

Pamiętaj, że React może nadal wymagać wyrenderowania tego konkretnego komponentu, zanim wymiga się od dalszych zmian. Nie powinno to być problemem, ponieważ React nie będzie niepotrzebnie wchodził „głębiej” w drzewo. Jeśli wykonujesz kosztowne obliczenia podczas renderowania, możesz je zoptymalizować za pomocą useMemo.

useEffect

useEffect(didUpdate);

Przyjmuje funkcję zawierającą imperatywny kod, mogący zawierać efekty uboczne.

W ciele głównej funkcji komponentu (określanej jako faza renderowania Reacta) nie jest dozwolone mutowanie danych, tworzenie subskrypcji, timerów, logowanie i inne efekty uboczne. Robiąc tak należy spodziewać się mylących błędów i niespójności w interfejsie użytkownika.

Zamiast tego użyj useEffect. Funkcja przekazana do useEffect zostanie uruchomiona po tym, jak zmiany zostaną wyświetlone się na ekranie. Traktuj efekty jako furtkę pomiędzy czysto funkcyjnym światem Reacta, a imperatywnym światem.

Domyślnie efekty są uruchamiane po każdym wyrenderowaniu komponentu, ale możesz sprawić, że uruchomią się tylko jeżeli zmienią się jakieś wartości.

Czyszczenie po efekcie

Często efekty tworzą zasoby (np. subskrypcję czy ID timera), które należy uprzątnąć zanim komponent opuści ekran. Aby to uczynić funkcja przekazywana do useEffect może zwracać funkcję czyszczącą. Na przykład przy tworzeniu subskrypcji:

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // Uprzątnij subskrypcję
    subscription.unsubscribe();
  };
});

Aby zapobiec wyciekom pamięci, funkcja czyszcząca wywoływana jest zanim komponent zostanie usunięty z interfejsu użytkownika. Dodatkowo jeśli komponent jest renderowany kilkukrotnie (co zwykle ma miejsce), poprzedni efekt jest czyszczony przed wywołaniem kolejnego efektu. W naszym przykładzie oznacza to, że nowa subskrypcja tworzona jest przy każdej aktualizacji. W kolejnym podrozdziale opisujemy, jak uniknąć wywoływania efektów przy każdej aktualizacji.

Harmonogram efektów

W przeciwieństwie do metod componentDidMount i componentDidUpdate funkcja przekazana do useEffect wywoływana zostanie po tym jak skomponowany i namalowany zostanie układ strony. Sprawia to, że nadaje się ona do obsługi wielu typowych efektów ubocznych, takich jak tworzenie subskrypcji czy obsługa zdarzeń. Większość tego typu operacji nie powinna powstrzymywać przeglądarki przed odświeżeniem widoku.

Jednakże nie wszystkie efekty mogą zostać odroczone. Na przykład manipulacja drzewem DOM, widoczna dla użytkownika, musi zostać wywołana synchronicznie przed każdym malowaniem, tak aby użytkownik nie dostrzegł wizualnej niespójności. (Rozróżnienie to w swej koncepcji podobne jest do pasywnych i aktywnych obserwatorów zdarzeń (ang. event listeners).) Dla tego typu efektów React przewiduje dodatkowy hook, nazwany useLayoutEffect. Ma on tę samą sygnaturę co useEffect, różnie się jedynie tym, kiedy jest wywoływany.

Pomimo, że wywołanie useEffect jest odraczane do czasu, kiedy przeglądarka zakończy malowanie, jest zagwarantowane, że zostanie wykonane przed każdym nowym renderem. React opróżni bufor efektów z poprzednich renderów, zanim rozpocznie nową aktualizację.

Warunkowe uruchamianie efektów

Domyślnym zachowaniem efektów jest ich uruchamianie po każdym pomyślnym renderze. W ten sposób efekt jest zawsze tworzony na nowo, jeśli zmieni się jedna z jego zależności.

Jednakże w pewnych przypadkach może się to okazać zabójcze - choćby w przykładzie subskrypcji z poprzedniego podrozdziału. Nie ma potrzeby tworzyć nowej subskrypcji przy każdej aktualizacji, a jedynie wtedy gdy zmieni się właściwość source.

Aby zaimplementować takie zachowanie należy przekazać do useEffect drugi argument, będący tablicą wartości, od których zależy efekt. Nasz zaktualizowany przykład wygląda następująco:

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

Teraz subskrypcja zostanie stworzona ponownie tylko wtedy, gdy zmieni się właściwość props.source.

Uwaga

Jeśli korzystasz z tej techniki optymalizacji, upewnij się, że przekazywana tablica zawiera wszystkie wartości z zasięgu komponentu (takie jak właściwości (ang. props) i stan), które zmieniają się w czasie i które są używane przez efekt. W przeciwnym razie twój kod odwoła się do starych wartości z poprzednich renderowań. Przeczytaj też, jak radzić sobie z funkcjami i co robić, gdy tablica zmienia się zbyt często.

Jeśli chcesz przeprowadzić efekt i posprzątać po nim tylko raz (podczas montowania i odmontowania), możesz przekazać pustą tablicę ([]) jako drugi argument. Dzięki temu React wie, że twój efekt nie zależy od jakichkolwiek wartości właściwości lub stanu, więc nigdy nie musi być ponownie uruchamiany. Nie jest to traktowane jako przypadek specjalny — wynika to bezpośrednio z tego, jak zawsze działa tablica wejść.

Jeśli przekażesz pustą tablicę ([]) właściwości i stan wewnątrz efektu zawsze przyjmą swoje początkowe wartości. Pomimo że przekazywanie [] jest bliższe temu, co znamy z metod componentDidMount i componentWillUnmount, zwykle istnieją lepsze rozwiązania pomagające uniknąć zbyt częstego powtarzania efektów. Nie zapominaj też, że React opóźni wywołanie useEffect do momentu, aż przeglądarka nie skończy rysować widoku, więc dodatkowa praca tutaj nie jest dużym problemem.

Polecamy użycie reguły exhaustive-deps, będącej częścią naszego pakietu eslint-plugin-react-hooks. Ostrzega ona, gdy zależności są niepoprawnie zdefiniowane i sugeruje poprawki.

Tablica zależności nie jest przekazywana jako argumenty do funkcji efektu. Koncepcyjnie jednak to właśnie przedstawiają: każda wartość, do której odwołuje się funkcja efektu, powinna również pojawić się w tablicy zależności. W przyszłości dostatecznie zaawansowany kompilator mógłby automatycznie tworzyć tę tablicę.

useContext

const value = useContext(MyContext);

Przyjmuje obiekt kontekstu (wartość zwróconą przez React.createContext) i zwraca jego aktualną wartość. Wartość kontekstu jest określana przez właściwość (ang. prop) value najbliższego rodzica <MyContext.Provider> wywołującego komponentu.

Kiedy najbliższy rodzic <MyContext.Provider> zostanie zaktualizowany, ten hook wywoła ponowne renderowanie komponentu z najnowszym kontekstem value przekazanym dostawcy (ang. provider) MyContext.

Pamiętaj, że argument przekazany do useContext musi być samym obiektem kontekstu:

  • Poprawnie: useContext(MyContext)
  • Niepoprawnie: useContext(MyContext.Consumer)
  • Niepoprawnie: useContext(MyContext.Provider)

Komponent wywołujący useContext będzie zawsze ponownie renderowany jeśli zmieni się wartość kontekstu. Jeżeli ponowne renderowanie danego komponentu jest kosztowne, możesz zoptymalizować to zachowanie, używając techniki zapamiętywania (ang. memoization).

Podpowiedź

Jeśli znasz już interfejs API kontekstu — useContext(MyContext) jest odpowiednikiem klasowego static contextType = MyContext lub też <MyContext.Consumer>.

useContext(MyContext) pozwala tylko na czytanie kontekstu i nasłuchiwanie jego zmian. Wciąż wymagane jest aby w drzewie, ponad komponentem, znalazł się <MyContext.Provider> by mógł dostarczyć (ang. provide) wartość tego kontekstu.

Zaawansowane hooki

Poniższe hooki są albo są wariantami tych podstawowych, z poprzedniego podrozdziału, albo są stosowane tylko w określonych skrajnych wypadkach. Nie stresuj się na myśl o nauce o nich.

useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);

Alternatywa dla hooka useState. Przyjmuje reduktor (ang. reducer), będący funkcją o sygnaturze (stan, akcja) => nowyStan, a zwraca aktualny stan w parze z metodą dispatch. (Jeżeli znasz bibliotekę Redux, wiesz już, jak to działa.)

useReducer sprawdza się lepiej od useState tam, gdzie występuje skomplikowana logika związana ze stanem, obejmująca wiele pod-wartości lub gdy następny stan zależy od poprzedniego. useReducer pozwala też zoptymalizować wydajność komponentów uruchamiających głębokie aktualizacje, ponieważ zamiast przekazywać funkcje zwrotne (ang. callback), możesz przekazać funkcję dispatch w dół drzewa.

Oto przykład licznika z podrozdziału useState przepisany z użyciem reduktora:

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Licznik: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

Uwaga

React daje gwarancje, że funkcja dispatch jest tożsamościowa i że nie zmienia się podczas kolejnych renderowań. Dlatego też można ją bezpiecznie pominąć na liście zależności useEffect i useCallback.

Określanie stanu początkowego

Istnieją dwa sposoby na inicjalizację stanu useReducer. W zależności od potrzeb, możesz wybrać jeden z nich. Najprostszym sposobem jest przekazanie początkowego stanu, jako drugi argument:

  const [state, dispatch] = useReducer(
    reducer,
    {count: initialCount}
  );

Uwaga

React nie używa spopularyzowanej przez Reduxa konwencji argumentu state = initialState. Może się zdarzyć, że początkowa wartość zależy od właściwości (ang. props), a więc jest ona określana przy wywoływaniu hooka. Nie zalecamy, ale jeśli naprawdę musisz, możesz wywołać useReducer(reducer, undefined, reducer), aby zasymulować zachowanie Reduxa.

Leniwa inicjalizacja

Możesz też leniwe zainicjalizować stan początkowy. Aby to zrobić, musisz przekazać funkcję inicjalizującą init, jako trzeci argument. Początkowy stan zostanie ustawiony na wynik wywołania init(initialArg).

Pozwala to na wyodrębnić logikę dotyczącą obliczania stanu początkowego poza reduktor. Jest to też przydatne do późniejszego resetowania stanu, w odpowiedzi na akcję:

function init(initialCount) {
  return {count: initialCount};
}

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  return (
    <>
      Licznik: {state.count}
      <button
        onClick={() => dispatch({type: 'reset', payload: initialCount})}>
        Zresetuj
      </button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

Wycofanie się z posłania akcji

Jeżeli reduktor zwróci tę samą wartość, jaką aktualnie przyjmuje stan, React „wymiga się” od aktualizacji potomków i uruchomienia efektów. (React używa algorytmu porównywania Object.is.)

Pamiętaj, że React może nadal wymagać wyrenderowania tego konkretnego komponentu, zanim wymiga się od dalszych zmian. Nie powinno to być problemem, ponieważ React nie będzie niepotrzebnie wchodził „głębiej” w drzewo. Jeśli wykonujesz kosztowne obliczenia podczas renderowania, możesz je zoptymalizować za pomocą useMemo.

useCallback

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

Zwraca zapamiętaną (ang memoized) funkcję zwrotną (ang. callback).

Przekaż funkcję zwrotną i tablicę zależności. useCallback zwróci zapamiętaną wersję funkcji, która zmieni się tylko wtedy, gdy zmieni się któraś z zależności. Jest to przydatne podczas przekazywania funkcji zwrotnych do zoptymalizowanych komponentów podrzędnych, które opierają się na równości referencji, aby zapobiec niepotrzebnym renderowaniom (np. shouldComponentUpdate).

useCallback(fn, deps) jest równoważne useMemo(() => fn, deps).

Uwaga

Tablica zależności nie jest przekazywana jako argumenty do funkcji zwrotnej. Koncepcyjnie jednak to właśnie przedstawiają: każda wartość, do której odwołuje się funkcja zwrotna, powinna również pojawić się w tablicy zależności. W przyszłości dostatecznie zaawansowany kompilator mógłby automatycznie tworzyć tę tablicę.

Polecamy użycie reguły exhaustive-deps, będącej częścią naszego pakietu eslint-plugin-react-hooks. Ostrzega ona, gdy zależności są niepoprawnie zdefiniowane i sugeruje poprawki.

useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

Zwraca zapamiętaną (ang memoized) wartość.

Przekaż funkcję tworzącą i tablicę zależności. useMemo obliczy ponownie zapamiętaną wartość tylko wtedy, gdy zmieni się któraś z zależności. Ta optymalizacja pozwala uniknąć kosztownych obliczeń przy każdym renderowaniu.

Pamiętaj, że funkcja przekazana do useMemo uruchamiana jest podczas renderowania. Nie należy w niej robić niczego, czego normalnie nie robiono by podczas renderowania. Na przykład wszelkie efekty uboczne przynależą do useEffect, a nie useMemo.

Jeśli nie zostanie przekazana żadna tablica, nowa wartość będzie obliczana przy każdym renderowaniu.

Możesz traktować useMemo jako metodę optymalizacji wydajności, nie jako semantyczną gwarancję. W przyszłości React może zdecydować się „zapomnieć” niektóre z wcześniej zapamiętanych wartości i ponownie obliczyć je przy następnym renderowaniu, np. aby zwolnić pamięć dla komponentów znajdujących się poza ekranem. Pisz swój kod tak, aby działał bez użycia hooka useMemo, a następnie dodaj go aby zoptymalizować wydajność.

Uwaga

Tablica zależności nie jest przekazywana jako argumenty do funkcji zwrotnej. Koncepcyjnie jednak to właśnie przedstawiają: każda wartość, do której odwołuje się funkcja zwrotna, powinna również pojawić się w tablicy zależności. W przyszłości dostatecznie zaawansowany kompilator mógłby automatycznie tworzyć tę tablicę.

Polecamy użycie reguły exhaustive-deps, będącej częścią naszego pakietu eslint-plugin-react-hooks. Ostrzega ona, gdy zależności są niepoprawnie zdefiniowane i sugeruje poprawki.

useRef

const refContainer = useRef(initialValue);

useRef zwraca mutowalny (ang. mutable) obiekt, którego właściwość .current jest inicjalizowana wartością przekazaną jako argument (initialValue). Zwrócony obiekt będzie trwał przez cały cykl życia komponentu.

Częstym przypadkiem użycia jest imperatywny dostęp do potomka:

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` wskazuje na zamontowany element kontrolki formularza
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Ustaw skupienie na kontrolce formularza</button>
    </>
  );
}

Zasadniczo useRef jest jak „pudełko”, które może przechowywać mutowalną wartość w swojej właściwości .current.

Być może znasz już referencje (ang. ref) jako sposób na dostęp do drzewa DOM. Jeśli przekażesz obiekt referencji do Reacta, korzystając ze składni <div ref={myRef} />, React ustawi jego właściwość .current na odpowiedni węzeł drzewa DOM przy każdej zmianie tego węzła.

Jednakże hook useRef() jest przydatny nie tylko jako atrybut ref. Jest też przydatną metodą na przechowywanie mutowalnej wartości, podobną do właściwości instancji w przypadku komponentów klasowych.

Sposób ten zadziała ponieważ useRef tworzy czysty javascriptowy obiekt. Jedyną różnicą pomiędzy wywołaniem useRef(), a samodzielnym tworzeniem obiektu {current: ...} jest to, że useRef zwróci referencję tego samego obiektu przy każdym renderowaniu.

Miej na uwadze fakt, że useRef nie informuje o tym, że jego wartość się zmieniła. Zmiana (mutowanie) właściwości .current nie spowoduje ponownego renderowania. Jeżeli chcesz uruchomić jakiś kod, w momencie gdy React dopina i odpina referencje do węzła DOM, możesz zamiast tego użyć funkcji zwrotnej.

useImperativeHandle

useImperativeHandle(ref, createHandle, [deps])

useImperativeHandle dostosowuje wartość instancji, jaka przekazywana jest komponentom przodków, kiedy używają właściwości ref. Jak zwykle zalecamy aby w większości przypadków unikać imperatywnego kodu korzystającego z referencji. useImperativeHandle powinien być użyty w parze z forwardRef:

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);

W tym przykładzie komponent rodzica, który renderuje <FancyInput ref={fancyInputRef} /> będzie w stanie wywołać fancyInputRef.current.focus().

useLayoutEffect

Sygnatura funkcji jest taka sama, jak useEffect, ale jest ona wywoływana jest synchronicznie po nałożeniu zmian na drzewo DOM. Użyj tego hooka aby odczytać układ (ang. layout) z drzewa DOM i synchronicznie wyrenderować komponent ponownie. Bufor useLayoutEffect zostanie opróżniony synchronicznie, zanim przeglądarka będzie miała szansę na malowanie.

Kiedy to tylko możliwe używaj useEffect aby uniknąć blokujących aktualizacji wizualnych.

Podpowiedź

Jeżeli przeprowadzasz migrację kodu z komponentu klasowego musisz wiedzieć, że useLayoutEffect uruchamiany jest w tych samych fazach, co componentDidMount i componentDidUpdate. Jednakże zalecamy zacząć od useEffect i używać useLayoutEffect tylko, jeżeli tamta metoda spowoduje jakieś problemy.

Jeżeli używasz renderowania po stronie serwera pamiętaj, że ani useLayoutEffect, ani useEffect nie może działać dopóki kod JavaScript nie zostanie pobrany. Dlatego React ostrzega jeżeli komponent renderowany po stronie serwera zawiera useLayoutEffect. Aby to naprawić możesz albo przenieść logikę korzystającą z useEffect (jeżeli nie jest niezbędna przy pierwszym renderze), albo opóźnić wyświetlanie tego komponentu do czasu renderowania po stronie klienta (jeżeli kod HTML wygląda na popsuty przed uruchomieniem useLayoutEffect).

Aby wyłączyć komponent, który korzysta z tego rodzaju efektów z wyrenderowanego po stronie serwera kodu HTML, wyrenderuj go warunkowo, korzystając z zapisu showChild && <Child /> i opóźnij jego wyświetlanie przy użyciu useEffect(() => { setShowChild(true); }, []). W ten sposób interfejs użytkownika nie będzie wyglądał na zepsuty przed odtworzeniem (ang. hydration).

useDebugValue

useDebugValue(value)

useDebugValue może zostać użyty do wyświetlania etykiet dla własnych hooków w narzędziu React DevTools.

Rozważ na przykład własny hook useFriendStatus opisywany w rozdziale “Tworzenie własnych hooków”:

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  // ...

  // Wyświetl etykietę w narzędziu DevTools obok tego hooka
  // np. "FriendStatus: Online"
  useDebugValue(isOnline ? 'Online' : 'Offline');

  return isOnline;
}

Podpowiedź

Nie zalecamy dodawania „debugowych” wartości każdemu własnemu hookowi. Jest to najbardziej przydatne w przypadku hooków będących częścią współdzielonych bibliotek.

Odroczenie formatowania „debugowych” wartości

W pewnych przypadkach formatowanie wartości może być kosztowną operacją. Jest też zbędne, dopóki hook nie jest rzeczywiście sprawdzany w narzędziach deweloperskich.

Dlatego też useDebugValue przyjmuje opcjonalnie jako drugi argument funkcję formatującą. Funkcja ta jest wywoływana tylko wtedy, gdy hooki są sprawdzane w narzędziach deweloperskich. Przyjmuje jako argument „debugową” wartość, a powinna zwrócić sformatowaną wartość.

Na przykład własny hook zwracający obiekt Date mógłby uniknąć niepotrzebnego wywołania funkcji toDateString poprzez przekazanie następującej funkcji formatującej:

useDebugValue(date, date => date.toDateString());