useReducer — przenoszenie logiki poza komponent

wp-content/uploads/2017/10/React_logo_wordmark-300x101-1-e1508612391308.png
  1. Wprowadzenie do kursu React.js 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
  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

useReducer to alternatywa dla useState. Ale po co i kiedy jej używać? W skrócie: Gdy logika komponentu się rozrasta i chcemy oddzielić ją całkowicie od widoku. Zgodnie z dobrymi praktykami!

Użycie useReducer

useReduceruseState pełnią bardzo podobne role: Służą do ustawiania stanu komponentu. Robią to jednak w inny sposób. Przypominam: useState przyjmuje stan początkowy, a zwraca aktualny stan i funkcję do ustawiania stanu:

const [count, setCount] = useState(0);

Więcej o tym doczytasz w artykule:

React Hooks: useState, czyli stan w komponentach funkcyjnych

Dla odmiany, useReducer jako argument przyjmuje reducer i stan początkowy, a zwraca stan oraz funkcję dispatch:

const [count, dispatch] = useReducer(countReducer, 0);

Jeśli znasz Reduksa, to na pewno od razu kojarzysz ten koncept!

Napiszmy reducer

Kontynuujmy przykład z prostym licznikiem. Tak, wiem, banały, ale od czegoś trzeba zacząć 🙄

function countReducer(state, action) {
    switch (action.type) {
        case 'increment':
            return state + 1;
        case 'decrement':
            return state - 1;
    }
}

function App() {
  const [count, dispatch] = React.useReducer(countReducer, 0);

  return (
    <div>
      {counter}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </div>
  );
};

Po kliknięciu w przyciski wysyłane są akcje: incrementdecrement. Są one obsługiwane przez countReducer, który odpowiednio zwiększa lub zmniejsza licznik o 1.

Reducer powinien zawsze zwracać nowy stan i nie modyfikować przekazanego argumentu.

Po co tak komplikować sobie życie?

Pewnie myślisz teraz: Eeee… ale po co? No i zgadzam się, w takim przypadku nie ma to najmniejszego sensu — kod jest dużo dłuższy i bardziej skomplikowany.

Kiedy więc użycie useReducer ma sens? Gdy tworzymy bardziej skomplikowany komponent, który może znaleźć się w wielu różnych stanach. Wtedy reducer sprawdzi się doskonale.

Ponadto, reducer można łatwo wynieść do osobnej funkcji, a nawet pliku i przetestować jednostkowo — kod staje się niezależny od Reakta i uniwersalny.

dispatch

Dodatkowo, warto zwrócić uwagę na nowe możliwości, które daje nam funkcja dispatch — możemy ją przekazać do innych komponentów w drzewie!

Jest to rozwiązanie znacznie lepsze i bezpieczniejsze niż przekazywanie w dół setState pozyskanej z useState. Dlaczego? setState może posłużyć do dowolnego nadpisania stanu, natomiast dispatch służy tylko i wyłącznie do wysyłania predefiniowanych zmian, które następnie muszą być przetworzone w reducerze.

Innymi słowy, przekazywanie dalej setState pozwala na dokonywanie dowolnych zmian w dowolnym miejscu (brzmi jak bałagan, no nie?), natomiast przekazanie dispatch ogranicza zmiany tylko do tych, które przewidzieliśmy — a do tego zachodzą one wyłącznie w reducerze!

Enkapsulacja poddrzewa komponentów

W praktyce, może się to przydać tam, gdzie tworzymy osobne komponenty, ale jednak związane ze sobą — np. ContactListContact. Najlepiej pokazać to na przykładzie:

See the Pen
React Hooks: useReducer
by Michał Miszczyszyn (@mmiszy)
on CodePen.

Pytania?

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

Podsumowanie

useReducer jest dobrym zamiennikiem useState w sytuacjach, gdy potrzebujemy wymodelować bardziej rozbudowane komponenty i ich stany. Możliwość łatwego wydzielenia reducera oraz testowania go jednostkowo to wisienka na torcie. useReducer na pewno się przyda!

Nawigacja po kursie:
  1. Wprowadzenie do kursu React.js 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
  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