Polub bloga na fejsie!

2 sposoby na wywołania asynchroniczne Redux

4

Dziś miał być wpis na temat RxJS w połączeniu z Redux. Jednak kiedy rozpocząłem pracę nad nim zauważyłem, że to co powinienem najpierw omówić to wywołania asynchroniczne Redux. Dlatego też zdecydowałem, że dziś przedstawię 2 sposoby na takie wywołania, a wpis o RxJS w Redux, pojawi się następnej kolejności (czyli prawdopodobnie w czwartek).

Zanim zaczniemy

Na potrzeby omówienia jak działają wywołania asynchroniczne Redux zakładam, że kwestie takie jak podstawy React czy Redux masz już opanowane. Jeśli tak nie jest, to zalecam najpierw zapoznać się z moimi wcześniejszymi wpisami, w których znajdziesz stosowną wiedzę z tego zakresu:

Przeczytanie wpisu na temat usprawnień Redux może nie jest absolutnie konieczne do zrozumienia treści dzisiejszego wpisu ale myślę, że mimo wszystko warto się z nim zapoznać. Ważne natomiast, abyś koniecznie znał zagadnienie middleware ponieważ, będzie to podstawą obu prezentowanych dziś rozwiązań!

Ok, skoro jesteś już przygotowany na to co dziś pokażę, przejdźmy teraz do pierwszego sposobu na wywołania asynchroniczne Redux!

Sposób pierwszy – redux-thunk

Najbardziej podstawowym sposobem realizacji wywołań asynchronicznych jest wykorzystanie specjalnego middleware o nazwie redux-thunk. Dotychczas nie omówiłem jeszcze tego middleware na blogu (było w planach)… Dlatego zanim pokażę jak wykorzystać go do wywołań asynchronicznych, zrobię małą dygresję i przedstawię Ci o co chodzi.

Dygresja: co to jest redux-thunk

Biblioteka redux-thunk, stworzona została przez Dana Abramova, który jednocześnie jest twórcą Reduxa. Pozwala ona tworzyć kreatory akcji, które zamiast obiektu zwracają funkcję. Dzięki temu możliwe jest opóźnienie rozgłoszenia (ang. „dispatch) akcji lub rozgłoszenie jej tylko jeśli zostaną spełnione określone warunki.

Najważniejsze w tym wszystkim jest to, że funkcja taka przyjmuje dwa parametry: dispatch oraz getState. Zanim jednak pokażę jak tego użyć w kreatorze akcji, rzućmy okiem na konfigurację tego middleware:

Jak widzisz, redux-thunk to zwykły middleware więc po prostu przekazujemy go jako parametr funkcji applyMiddleware tak jak to robimy z każdym innym middleware.

Spójrz zatem teraz na przykład kreatora akcji wykorzystującego redux-thunk:

Powyższy kod nie jest specjalnie wyszukany. Najważniejsze jest to, że funkcja getData zwraca funkcję przyjmującą parametr dispatch, który umożliwia nam rozgłoszenie innych akcji w zależności od parametru parameter. Za chwilę wykorzystamy tę właściwość redux-thunk przy wywołaniach asynchronicznych. Pewnie nawet domyślasz się już na jakiej zasadzie to będzie działać…

Obsługa wywołań asynchronicznych – kreatory akcji

Ok, wiemy już co to jest redux-thunk i jak działa. Wykorzystajmy go teraz do obsługi wywołań asynchronicznych. Na początek spójrz na przykładowe kreatory akcji:

Omówienie kodu

Na początku importuję funkcję fetch należącą do biblioteki isomorphic-fetch, która posłuży nam do wywołań AJAX. Oczywiście możesz użyć dowolnej innej biblioteki – to jest tylko przykład.

Następna rzecz to eksport stałych zawierających nazwy typów akcji. Zwróć uwagę, że mamy tutaj trzy typy: GET_DATA_REQUESTED będzie wywołany na początku operacji pobierania danych. Przestawimy wtedy „store” w stan ładowania danych. Kolejny typ to GET_DATA_DONE. Akcja tego typu spowoduje zakończenie stanu ładowania i zapisanie danych w store. Ostatni typ to GET_DATA_FAILED, który wykorzystamy w akcji wywoływanej w przypadku niepowodzenia operacji pobierania danych.

POLUB BLOGA NA FACEBOOKU!

Chcesz być na bieżąco informowany o nowościach na blogu oraz innych ciekawych treściach? Polub fanpage bloga na Facebooku!

Dalej mamy trzy kreatory akcji, odpowiedzialne za utworzenie akcji opisanych przed chwilą typów. Myślę, że nie trzeba ich dodatkowo wyjaśniać ponieważ, wszystko co trzeba znajdziesz w moich poprzednich wpisach o Redux.

To co najciekawsze w powyższym przykładzie to implementacja funkcji getData. Jak widzisz, zwraca ona funkcję przyjmującą parametr dispatch (można tutaj przyjąć również dodatkowo parametr getState ale nie jest nam to w tym momencie potrzebne). W pierwszej linii tej funkcji rozgłaszamy akcję GET_DATA_REQUESTED (poprzez wywołanie funkcji getDataRequested()). Możemy w ten sposób obsłużyć rozpoczęcie ładowania danych i przedstawić to na ekranie na przykład, wyświetlając animowany „loader”.

Dalej następuje wywołanie funkcji fetch, która „strzela” do publicznego API serwisu GitHub i pobiera listą moich publicznych repozytoriów. To co najważniejsze, dzieje się w przypadku pomyślnego odebrania danych (drugi then) oraz w przypadku błędu (callback przekazany do catch). W pierwszym przypadku rozgłaszamy akcję GET_DATA_DONE, a w drugim GET_DATA_FAILED. Dzięki temu będziemy mogli odpowiednio zmodyfikować stan w „reducerze” w zależności od wyniku wywołania asynchronicznego.

Obsługa w reducerze

Jeśli dobrze rozumiesz już zasadę działania Reduxa (na przykład po przeczytaniu poprzednich moich wpisów na ten temat), to pewnie w tym momencie rozumiesz już jak to wszystko działa. Dla porządku jednak przedstawię Ci resztę kodu, który odpowiedzialny jest za wywołania asynchroniczne Redux. Najpierw kod „reducera”:

Dla akcji GET_DATA_REQUESTED zwracam stan, w którym zmieniam tylko wartość właściwości isLoading na true.

Z kolei obsługując akcję GET_DATA_DONE przywracam stan początkowy właściwości isLoading (ustawiam wartość false) oraz przypisuję dane pobrane z API do właściwości repositories.

W przypadku gdy pobieranie danych zakończy się rozgłoszeniem akcji GET_DATA_FAILED, wartość właściwości isLoading również jest ustawiana z powrotem na false. Dodatkowo zmieniam wartość właściwości isError na true tak aby móc odpowiednio odzwierciedlić tę sytuację na ekranie.

Pozostały kod: konfiguracja „store”, wiązanie z this.props, komponent React

Na koniec przedstawiam pozostały kod, w którym konfiguruję „store” Reduxa, wiążę stan ze zmienną this.props komponentu oraz renderuję komponent na ekranie:

Myślę, że nie ma sensu dokładnie tutaj wszystkiego omawiać – jest to kod podobny do tego jaki przedstawiłem w moim wpisie na temat podstaw Reduxa. Zwróć jedynie uwagę, na wywołanie metody createStore gdzie przekazuję wywołanie funkcji applyMiddleware z parametrem thunk.

Kolejna rzecz warta odnotowania to komponent React. W metodzie render odnoszę się do właściwości repositories stanu Reduxa wyświetlając nazwy poszczególnych repozytoriów na ekranie. Na potrzeby przykładu pominąłem obsługę właściwości isLoading oraz isError. Jeśli chcesz, możesz dodać odpowiedni kod w ramach zadania domowego.

Kod przedstawionego przykładu dostępny jest na GitHubie – możesz go sobie sklonować i przeanalizować we własnym zakresie.

Sposób drugi – redux-promise-middleware

Oprócz redux-thunk istnieje jeszcze inne przydatne middleware o nazwie redux-promise-middleware. Spójrz najpierw na jego konfigurację:

W pierwszej linii następuje import funkcji promiseMiddleware z biblioteki redux-promise-middleware. Oczywiście jest ona dostępna w npm i instaluje się ją w sposób najzupełniej standardowy (npm install --save-dev ...).

W dalszej części przykładu, do metody applyMiddleware przekazujemy wywołanie funkcji promiseMiddleware() – możemy tutaj przekazać dodatkowe parametry ale o tym za chwilę.

Kreatory akcji

Przejdźmy teraz do pliku zawierającego kreatory akcji z przykładu dla redux-thunk i zmodyfikujmy go trochę:

Jak widzisz, zmieniły się co nieco nazwy typów akcji. Do tego zniknęły kreatory akcji korzystające z tych typów… To dlatego, że redux-promise-middleware dodaje obsługuje to wszystko we własnym zakresie.

Pozostał nam jedynie kreator akcji o nazwie getData. Jednak trochę się nam on uprościł: tym razem nie zwracamy funkcji, a obiekt akcji z typem ustawionym na wartość GET_DATA. Oprócz tego przekazujemy jako właściwość payload obiekt, który zawiera właściwość promise, do której przypisane zostało wywołanie funkcji fetch.

Zmiany w reducerze

Jak wspomniałem, opisywany middleware obsługuje poszczególne kreatory akcji we własnym zakresie. Działa to w ten sposób, że dla typu GET_DATA, doklejany jest sufiks, dla poszczególnych akcji. A więc, rozgłaszając akcję GET_DATA, tak na prawdę, rozgłaszana jest akcja GET_DATA_PENDING na starcie operacji, potem dla operacji zakończonej sukcesem rozgłaszana jest akcja GET_DATA_FULFILLED, a dla przypadku z błędem rozgłasza się akcja GET_DATA_REJECTED.

Z tego też względu musimy zmodyfikować też nasz reducer, tak by obsługiwał nowe typy akcji:

Zmiana domyślnych sufiksów

Na szczęście nie jesteśmy ograniczeni do wspomnianych sufiksów _PENDING, _FULFILLED oraz _REJECTED. Możemy skonfigurować własne sufiksy przy tworzeniu „store” Reduxa:

Jak widzisz, wystarczy podczas wywołania funkcji promiseMiddleware() przekazać do niego obiekt, jako parametr wywołania, który zawiera właściwość promiseTypeSuffixes. Do właściwości tej przypisać wystarczy tablicę zawierającą sufiksy dla poszczególnych typów akcji. W powyższym przykładzie ustawiłem sufiksy, które odpowiadają tym z przykładu dla redux-thunk.

Wywołania asynchroniczne Redux – podsumowanie

Jak widzisz, wywołania asynchroniczne Redux to nic trudnego. Pierwsze z przedstawionych podejść jest najbardziej podstawowym i warto dobrze je poznać. Drugie stosowałem osobiście w jednym z projektów i sprawdza się całkiem dobrze. Są jednak sytuacje kiedy i tak przydaje się redux-thunk. Dlatego też znajomość tego middleware jest bardzo ważna przy pracy z Reduxem.

Dzięki temu wpisowi, w kolejnym wpisie będę mógł pójść krok dalej i omówić wykorzystanie RxJS wraz z Redux. Myślę, że dzięki temu temat Redux będzie jednym z lepiej opisanych na tym blogu!

REACT, REDUX, REACT-ROUTER - KURSY ON-LINE

Chcesz od podstaw poznać tajniki React, Redux oraz react-router? Zapraszam do moich szkoleń on-line:

Przejdź do szkoleń

Uwaga! Obecnie trwa przedsprzedaż kursów - premiera 1 sierpnia 2017!

  • Robert

    Cześć!

    Wciągnąłem wszystkie części jednych tchem. Początkowo „obawiałem się”, że piszesz po polsku, ale po chwili wszystkie wątpliwości zniknęły, bo nie próbujesz ich na siłę spolszczać. Super blog, świetne wpisy – będę śledził! 🙂

    Odnośnie tego artykułu – słyszałeś, używałeś może Redux-Saga? Może warto byłoby również porównać?

  • Tomasz Trzaskoma

    Dzięki, bardzo to przystępnie wyjaśnione. W tym momencie używam thunk + axios. Myślę, że zestaw ten ogrywa większośc przypadków wywołań ajax 🙂

    • szczerze mówiąc nie znam axios 😉 ale tych bibliotek jest sporo więc można znaleźć coś dla siebie – dzięki za komentarz!

Google Analytics Alternative