Bevezetés a funkcionális reaktív programozásba a Redux-ban

Kezdjük azzal, hogy megkapjuk az alapgondolatot arról, hogy mi a „reaktív programozás”:

A reaktív programozás aszinkron programozási paradigma, amely az adatfolyamokkal és a változás terjedésével foglalkozik.

- Wikipédia

A ReactiveX vagy Rx a legnépszerűbb API a reaktív programozáshoz. A megfigyelhető minta, az iterátor minta és a funkcionális programozás ideológiáira épül. Az Rx rendelkezik könyvtárakkal különböző nyelvekhez, de mi az RxJS-t fogjuk használni.

Az Rx az Observables , Observers és Operators alapokon nyugszik

A Megfigyelő lényegében egy Megfigyelhetőre előfizet .

A Megfigyelhető ekkor olyan adatfolyamokat bocsát ki, amelyeket a Megfigyelő hallgat, és amelyekre reagál, mozgásba hozva az adatfolyamon a műveletek láncolatát. A valódi hatalom az Operátorokból vagy a „Reaktív kiterjesztésekből” származik (innen az Rx kifejezés) .

Az operátorok lehetővé teszik, hogy átalakítsák, kombinálják, manipulálják és dolgozhassanak az Observables által kibocsátott elemek sorozatával.

Ha nem ismeri az Rx-t, akkor nehezen tudja megérteni és használni a Redux-Observable alkalmazást. Tehát azt javaslom, hogy először piszkolja be a kezét az Rx-szel!

Most az RxJS és a Redux együttes használatára.

Redux-megfigyelhető

A Redux-Observable egy RxJS alapú köztes szoftver a Redux számára

Ezt mondják a Redux Docs-ok a Redux köztes szoftveréről:

A Middleware egy harmadik féltől származó kiterjesztési pontot biztosít egy művelet kiküldése és a reduktorig való elérés pillanatában.

A Redux middleware használható naplózásra, hibajelentésre, aszinkron API-val való beszélgetésre, útválasztásra és egyebekre. Vagy általában mellékhatásokat is mondhatunk .

Tehát hogyan teszi mindezt a Redux-Observable?

Az Epics révén. Az eposzok a Redux-Observable alapvető primitívjei. Az eposz csak egy egyszerű funkció, amely egy műveletet hajt végre, majd egy másik műveletet ad vissza. Művelet be → Művelet ki . A műveleteket ezért patakként kezeljük.

A React bármely összetevőjében elküldött minden cselekvés áramlatként halad át az ilyen funkciókon (Epics).

Lássuk, hogyan néz ki egy egyszerű eposz, amely újaction'PING’ és újaction'PONG’ képet ad :

const pingEpic = action$ => action$.filter(action => action.type === 'PING') .mapTo({ type: 'PONG' })

Az $after actionazt jelzi, hogy ezek a változók folyamokra hivatkoznak. Tehát olyan cselekvési folyamatot viszünk át az filterEpicbe, amelyen az RxJS operátorát használtuk .

Ez a szűrő operátor kiszűri az összes olyan műveletet, amely nem a typePING! Ezért az eposz pingEpiccsak a type‘PING’. Végül ez action‘PING’egy új action, az type‘PONG’Epics kielégítő fő szabályának felel meg: Action In → Action Out .

Mivel minden eposz csak egy adott típusú akcióval foglalkozik, van egy speciális operátorunk a action$(stream) számára, amely kiszűri a nem kívánt műveleteket a streamből. Ez az operátor az ofType()operátor.

Az előző eposz átírása a következő módon ofType:

const pingEpic = action$ => action$.ofType('PING') .mapTo({ type: 'PONG' })

Ha azt szeretné, epikus, hogy több mint egy fellépésre, az ofType()üzemeltető megteszi a számos érv így: ofType(type1, type2, type3,...).

Az eposzok működésének sajátosságai

Gondolhatja, hogy a „PING” akció egyszerűen bejön és megemészti ezt az eposzt. Ez nem így van. Két dologra kell mindig emlékezni:

  1. Minden művelet először a reduktorhoz megy
  2. Csak ezt követően kapja meg az eposz azt a cselekedetet

Ezért a Redux ciklus normálisan működik, ahogy kellene.

A action‘PING’eléri a szűkítő első, majd megkapta az Epic, majd megváltoztatta egy új action‘PONG’amelyek elküldik a szűkítő.

Akár az Epic belsejében is elérhetjük a bolt állapotát, mert az Epic második érve a Redux Store könnyű változata! Lásd alább:

const myEpic = (action$, store) =>

Csak ll store.getStatelérhetjük () és elérhetjük az Epics belsejében lévő állapotot.

Operátor láncolás

Az akció fogadása és az új küldése között mindenféle aszinkron mellékhatást megtehetünk, például AJAX-hívásokat, webaljzatokat, időzítőket stb. Ez az Rx által biztosított számos operátor segítségével történik .

Ezek az Rx operátorok lehetővé teszik az aszinkron szekvenciák együttes, deklaratív módon történő összeállítását, a visszahívások összes hatékonysági előnyével, de a fészkelő visszahívás-kezelők hátrányai nélkül, amelyek jellemzően aszinkron rendszerekhez vannak társítva.

Kihasználjuk a visszahívások előnyeit, anélkül, hogy ez a hírhedt visszahívási pokol lenne.

Az alábbiakban nézze meg, hogyan tudjuk kihasználni az operátorok erejét.

Gyakori felhasználási eset

Tegyük fel, hogy egy szót akarunk keresni valami hasonlóval, mint egy szótár API, a felhasználó által valós időben beírt szöveg felhasználásával. Alapvetően az (Redux áruházban) tárolással és az API hívás eredményeinek megjelenítésével foglalkozunk. Szeretnénk visszavonni az API hívást is, hogy az API-t mondjuk 1 másodpercen belül meghívjuk, amikor a felhasználó abbahagyja a gépelést.

Az Epic és az RxJS operátorok segítségével így fog történni:

const search = (action$, store) => action$.ofType('SEARCH') .debounceTime(1000) .mergeMap(action => ajax.getJSON(`//someapi/words/${action.payload}`) .map(payload => ({ type: 'SET_RESULTS', payload })) .catch(payload => Observable.of({type: 'API_ERROR', payload})) )

Túl sok kezelni ?! Ne aggódj, bontsuk le ezt.

Az eposz minden cselekvési folyamot kap oftype‘SEARCH’. Mivel a felhasználó folyamatosan gépel, minden bejövő művelet ( action.payload) hasznos terhe tartalmazza a frissített keresési karakterláncot.

The operator debounceTime() is used to filter out some of the actions in the stream except the last one. It basically passes an action through it only if 1 second has elapsed without it receiving another action or observable.

We then make the AJAX request, mapping the results to another action 'set_RESULTS' which takes the response data (payload) to the reducer, which is the Action Out part.

Any API errors are caught using the catch operator. A new action is emitted with the error details and later displays a toaster with the error message.

Notice how the catch is inside the mergeMap() and after the AJAX request? This is because the mergeMap() creates a chain that is isolated. Otherwise the error would reach ofType() and will terminate our Epic. If that happens, the Epic will stop listening to any action in the future!

We can use traditional promises for AJAX requests as well. However, they have this inherent problem of not being able to get cancelled. So another important use case for using Epics is AJAX cancellation.

We use the takeUntil operator to handle this issue. This is done just like we used that catch operator inside mergeMap and after the AJAX request.

This is because takeUntil must stop the current AJAX request and not the entire Epic! Therefore, isolating operator chains is important here as well.

Debouncing, throttling, filtering, AJAX cancellation and others, are just the tip of the iceberg. We have a myriad of operators at our disposal, making difficult use-cases trivial to solve. Using these operators, you can get as creative as your imagination allows you to be! Functional Reactive Programming (FRP) is elegant in its own way.

My focus for this article was on the explanation part of FRP in Redux using Redux-Observable. For setting up Redux-Observable in React+Redux, refer to the official docs — its very well documented, detailed, and easy-breezy.

Be sure to check out my other article on Redux which explores the best practice for creating reducers:

Reducing the Reducer Boilerplate With createReducer()

First, a quick recap of what reducers in Redux are:medium.freecodecamp.org

Want to improve your JavaScript basics? Give these a read:

JavaScript ES6 Functions: The Good Parts

ES6 offers some cool new functional features that make programming in JavaScript much more flexible. Let’s talk about…medium.freecodecamp.orgA guide to JavaScript variable hoisting ? with let and const

New JavaScript developers often have a hard time understanding the unique behaviour of variable/function hoisting.medium.freecodecamp.org Function Hoisting & Hoisting Interview Questions

This is a part 2 for my previous article on Variable Hoisting titled “A guide to JavaScript variable hoisting ? with…medium.freecodecamp.org

Peace ✌️