Lebrande
I 🍻 web

Przetwarzanie strumieni z RxJS i redux-observable

January 05, 2018

Czym jest RxJS?

RxJS to biblioteka JavaScript pozwalająca programować reaktywnie. Jest to alternatywa do pisania kodu opartego o callbacki. RxJS tworzy i przetwarza strumienie danych, a Twoim zadaniem jest reagowanie na dane w nim zawarte i odpowiednie komponowanie wielu strumieni ze sobą. Jest to powiązane z programowaniem funkcyjnym, czyli dość trudną sztuką, ale przynoszącą benefity. Kodu jest mniej, a kiedy już zrozumiemy o co chodzi, widzimy wiele możliwości. Pisanie staje się przyjemnością 🍻

Podobnie jest z RxJS. Zrozumienie jak działają jego poszczególne operatory wymaga od Ciebie praktyki. W samej dokumentacji jest to ukazane bardziej obrazowo niż technicznie. Aby je poznać, spróbuj coś napisać. W moim przykładzie pokazuję jak możesz odebrać dane wpisywane do inputa.

const input = document.getElementById('input')
const output = document.getElementById('output')

const inputEvent$ = Rx.Observable.fromEvent(input, 'input')

inputEvent$.subscribe(e => {
	output.innerHTML = e.target.value
})

Zobacz na JSFiddle

Observable

Umawiamy się, że słowa “observable” nie da się przetłumaczyć na język polski.

Zakładam, że pod input kryje się jakiś input z drzewa DOM. Metodą Rx.Observable.fromEvent tworzysz strumień zdarzeń inputEvent$. Następnie za pomocą subscribe podpinasz się do strumienia i przetwarzasz jego elementy.

Przykład operatora map

const input = document.getElementById('input')
const output = document.getElementById('output')

const inputEvent$ = Rx.Observable.fromEvent(input, 'input') 

const text$ = inputEvent$.map(e => {
  return e.target.value
})

text$.subscribe(text => {
  output.innerHTML = text
})

Zobacz na JSFiddle

Operator map pozwolił Ci przechwycić strumień inputEvent$ i utworzyć na jego podstawie nowy text$. Umownie zapisuje się znak dolara na końcu nazwy zmiennej, aby zobrazować, że jest to właśnie strumień. Tak właśnie podzieliłeś sobie wykonywane zadanie na dwa strumienie 🍻

redux-observable

Narzędzie redux-observable to middleware do reduxa, które pozwala na przechwycenie strumienia akcji. Akcje może przechwycić zarówno reducer jak i epic, w podanej właśnie kolejności. Pojęcie epic w kontekście redux-observable to coś, jak użyty wcześniej observable. Epic podpinasz do stora, co pokażę dalej.

const peopleEpic = action$ => action$.ofType(FETCH_PEOPLE)
  .mergeMap(action => Observable.ajax.get(url)
  .map(({response}) => fetchPeopleFulfielled(response))
  .catch(error => Observable.of(fetchPeopleRejected(error.message)))
  .takeUntil(action$.ofType(FETCH_PEOPLE_CANCELLED))
)

peopleEpic wyłapuje akcje poprzez ofType. Jest to akcja asynchroniczna, pobieramy dane za pomocą http get. Dalej użyj operatorów mergeMap, map, catch, takeUntil. Najtrudniejszy z tego przykładu jest mergeMap. W pozostałych można domyślić się o co chodzi, catch wychwytuje błędy, a takeUntil pozwala tu na anulowanie observable. Jest to przewaga nad promisami. Tak jak wspomniałem wcześniej, musisz tego spróbować. Wynikowy kod jest jednak bardzo zwięzły i dość elegancko napisany.

const { createEpicMiddleware } = ReduxObservable
const { createStore, applyMiddleware } = Redux

const epicMiddleware = createEpicMiddleware(peopleEpic)
const store = createStore(
  peopleReducer, 
  applyMiddleware(epicMiddleware)
)

Teraz możesz podpiąć peopleEpic do Twojego stora jako middleware. Użyj metody createEpicMiddleware jaką dostarcza redux-observable. Jeśli masz kilka obiektów epic, połącz je podobnie jak reducery poprzez combineEpic 🍺

Pełny przykład na JSFiddle


Kyle Mathews

Written by Jakub Pusiak. Follow me on Twitter