useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami

wp-content/uploads/2017/10/React_logo_wordmark-300x101-1-e1508612391308.png
  1. React.js: Wprowadzenie do kursu od podstaw
  2. Poznaj React.js
  3. Pierwszy komponent w React.js
  4. Props czyli atrybuty w React.js
  5. Podział na komponenty w React.js
  6. Klasy jako komponenty React.js
  7. Interakcja z komponentami React.js
  8. Stan komponentów React.js
  9. State w React.js 2
  10. Metody cyklu życia komponentu w React.js
  11. React.js w przykładach: filtrowanie statycznej listy
  12. Tworzenie aplikacji React.js dzięki create-react-app
  13. React.js na GitHub Pages dzięki create-react-app
  14. Testowanie aplikacji React.js — podstawy Enzyme
  15. Testowanie React.js w Enzyme — props, state i interakcje
  16. Poprawne bindowanie funkcji w React.js
  17. Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
  18. Komunikacja pomiędzy komponentami w React.js
  19. Komunikacja z API w React.js
  20. Formularze w React.js — kontrolowane komponenty
  21. Formularze w React.js — niekontrolowane komponenty
  22. Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
  23. TDD w React.js z pomocą react-testing-library
  24. Flux i Redux: globalny store i jednokierunkowy przepływ danych
  25. React + Redux — kurs: wprowadzenie i podstawy
  26. React + Redux — filtrowanie listy, proste selektory
  27. Projektowanie komponentów: Presentational & Container Components
  28. Asynchroniczność w Redux: redux-thunk
  29. Kiedy używać state, a kiedy Redux?
  30. Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
  31. Leniwe ładowanie komponentów w React dzięki import
  32. Higher Order Reducers — Redux i powtarzanie kodu
  33. React Hooks — wprowadzenie i motywacja
  34. React Hooks: useState, czyli stan w komponentach funkcyjnych
  35. React Hooks: useState — wiele stanów, callbacki i inne niuanse
  36. React Hooks: useEffect — efekty uboczne w komponencie
  37. React Hooks a żądania do API
  38. useReducer — przenoszenie logiki poza komponent
  39. useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami

Powszechnym zmartwieniem osób poznających komponenty funkcyjne jest tworzenie funkcji-callbacków przekazywanych dalej jako props. Wszak przy każdym renderze funkcja tworzona jest na nowo! Czy to aby nie marnotrastwo? Czy nie powoduje to problemów? React Hooks useMemouseCallback przychodzą na ratunek!

Problem z funkcjami

Weźmy prosty komponent, w którym tworzony jest callback handleClick przekazywany jako props dalej:

function MyComponent({ oneProp, anotherProp }) {
    function handleClick() {
        console.log('Clicked!', oneProp, anotherProp);
    }

    return <SpecialButton onClick={handleClick} />;
}

Gdy ja pierwszy raz zobaczyłem taki kod, miałem mnóstwo wątpliwości — Ale po co? Dlaczego? Jak to? Przecież to nie ma sensu! Chętnie odpowiem Ci na te pytania osobiście: Poznaj React na szkoleniu!

Przede wszystkim, tworzenie nowej funkcji handleClick za każdym razem, gdy renderowany jest komponent wydawało mi się barbarzyństwem. Czy to aby nie ma tragicznego wpływu na wydajność?

No i dodatkowo, tworzymy nową funkcję, więc jako props do komponentu SpecialButton przekazywana jest zawsze nowa referencja. Oznacza to, że zostanie on przerenderowany za każdym razem.

Problemy z obiektami

Analogiczna sytuacja występuje, o czym wiele osób zdaje się nie myśleć, gdy w renderze tworzymy nowy obiekt:

function MyComponent({ oneProp, anotherProp }) {
    const options = {
        data: oneProp,
        data2: anotherProp,
        // …
    };

    return <SpecialComponent options={options} />;
}

Za każdym razem options tworzone jest na nowo, i jest nową referencją. Po przekazaniu tego obiektu jako props do SpecialComponent, ten przerenderuje się za każdym razem.

useCallback

Jest taki React Hook, który rozwiązuje pierwszy problem! Nazywa się useCallback. Jako pierwszy argument przyjmuje funkcję, a jako drugi taką samą tablicę zależności, jak useEffect.

function MyComponent({ oneProp, anotherProp }) {
    const handleClick = React.useCallback(() => {
        console.log('Clicked!', oneProp, anotherProp)
    }, [oneProp, anotherProp]);

    return <SpecialButton onClick={handleClick} />;
}

W ten sposób, funkcja przekazana do React.useCallback jest memoizowana. To znaczy, że dopóki nie zmienią się oneProp lub anotherProp, dopóty handleClick nie będzie nową funkcją.

React.useCallback zapamiętuje stworzoną funkcję i dzięki temu komponent SpecialButton nie przerenderuje się bez potrzeby!

useMemo

To React Hook, który zwraca zapamiętaną wartość. W pewnym sensie, to uogólniona wersja useCallback. Jako argument przyjmuje funkcję, która zwraca wartość.

function MyComponent({ oneProp, anotherProp }) {
    const options = useMemo(() => ({
        data: oneProp,
        data2: anotherProp,
        // …
    }), [oneProp, anotherProp]);

    return <SpecialComponent options={options} />;
}

W ten sposób obiekt options będzie za każdym razem tą samą referencją i komponent SpecialComponent nie będzie musiał się przerenderowywać — przynajmniej dopóki nie zmieni się oneProp lub anotherProp!

Czy Hooki useCallback i useMemo nie są zbędne?

Napisałem, że React Hook useMemo to trochę uogólniona wersja useCallback — i to prawda. useCallback jest funkcjonalnie równoważny takiemu zapisowi:

const myUseCallback = (fn, deps) => useMemo(() => fn, deps)

Kiedy używać

Z rozsądkiem 😉 Nie powiem Ci dokładnie kiedy, ale ja bym używał zawsze, gdy może być z tego jakiś zysk, a nie ma negatywnego wpływu na czytelność.

Pytania?

Jeśli chcesz na bieżąco śledzić kolejne części kursu React.js to koniecznie polub mnie na Facebooku i zapisz się na newsletter.

Nawigacja po kursie:
  1. React.js: Wprowadzenie do kursu od podstaw
  2. Poznaj React.js
  3. Pierwszy komponent w React.js
  4. Props czyli atrybuty w React.js
  5. Podział na komponenty w React.js
  6. Klasy jako komponenty React.js
  7. Interakcja z komponentami React.js
  8. Stan komponentów React.js
  9. State w React.js 2
  10. Metody cyklu życia komponentu w React.js
  11. React.js w przykładach: filtrowanie statycznej listy
  12. Tworzenie aplikacji React.js dzięki create-react-app
  13. React.js na GitHub Pages dzięki create-react-app
  14. Testowanie aplikacji React.js — podstawy Enzyme
  15. Testowanie React.js w Enzyme — props, state i interakcje
  16. Poprawne bindowanie funkcji w React.js
  17. Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
  18. Komunikacja pomiędzy komponentami w React.js
  19. Komunikacja z API w React.js
  20. Formularze w React.js — kontrolowane komponenty
  21. Formularze w React.js — niekontrolowane komponenty
  22. Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
  23. TDD w React.js z pomocą react-testing-library
  24. Flux i Redux: globalny store i jednokierunkowy przepływ danych
  25. React + Redux — kurs: wprowadzenie i podstawy
  26. React + Redux — filtrowanie listy, proste selektory
  27. Projektowanie komponentów: Presentational & Container Components
  28. Asynchroniczność w Redux: redux-thunk
  29. Kiedy używać state, a kiedy Redux?
  30. Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
  31. Leniwe ładowanie komponentów w React dzięki import
  32. Higher Order Reducers — Redux i powtarzanie kodu
  33. React Hooks — wprowadzenie i motywacja
  34. React Hooks: useState, czyli stan w komponentach funkcyjnych
  35. React Hooks: useState — wiele stanów, callbacki i inne niuanse
  36. React Hooks: useEffect — efekty uboczne w komponencie
  37. React Hooks a żądania do API
  38. useReducer — przenoszenie logiki poza komponent
  39. useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami