En introduktion til funktionel reaktiv programmering i Redux

Lad os starte med at få den grundlæggende idé om, hvad "Reaktiv programmering" er:

Reaktiv programmering er et asynkront programmeringsparadigme, der beskæftiger sig med datastrømme og forplantning af ændringer.

- Wikipedia

ReactiveX eller Rx er den mest populære API til reaktiv programmering. Det er bygget på ideologierne om det observerbare mønster, itteratormønster og funktionel programmering. Rx har biblioteker til forskellige sprog, men vi bruger RxJS.

Rx er baseret på observerbare , observatører og operatører

En observatør abonnerer i det væsentlige på en observerbar.

Den observerbare udsender derefter strømme af data, som observatøren lytter til og reagerer på, idet den sætter en kæde af operationer i gang i datastrømmen. Den virkelige kraft kommer fra operatører eller “reaktive udvidelser” (deraf udtrykket Rx) .

Operatører giver dig mulighed for at transformere, kombinere, manipulere og arbejde med sekvenserne af emner udsendt af Observables.

Hvis du ikke er fortrolig med Rx, har du muligvis svært ved at forstå og bruge Redux-Observable. Så jeg foreslår, at du først får dine hænder beskidte med Rx!

Nu til brug af RxJS med Redux.

Redux-observerbar

Redux-Observable er en RxJS-baseret middleware til Redux

Dette er hvad Redux Docs har at sige om middleware i Redux:

Middleware giver et tredjepartsudvidelsespunkt mellem afsendelse af en handling og det øjeblik det når reduktionsværktøjet.

Redux middleware kan bruges til logning, rapportering om nedbrud, samtale med en asynkron API, routing og mere. Eller vi kan sige bivirkninger generelt.

Så hvordan gør Redux-Observable alt det?

Gennem Epics. Epics er kernen primitive i Redux-Observable. Et epos er bare en simpel funktion, der udfører en handling og derefter returnerer en anden handling. Handling ind → Handling ud . Handlinger behandles derfor som vandløb.

Hver handling, der afsendes i en hvilken som helst komponent i React, vil passere gennem sådanne funktioner (Epics) som en stream.

Lad os se, hvordan et simpelt Epic, der tager et action'PING’og returnerer et nyt,action'PONG’ ser ud:

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

Den $efter actionanvendes til at indikere, at disse variable refererer streams. Så vi har en strøm af handlinger, der sendes til Epic, hvor vi har brugt filteroperatøren af ​​RxJS.

Denne filteroperatør filtrerer alle de handlinger, der ikke hører til typePING! Derfor er Epic pingEpickun beskæftiget med håndtering af handlinger af type‘PING’. Endelig action‘PING’kortlægges dette til et nyt actionaf det type‘PONG’tilfredsstillende hovedreglen i Epics: Action In → Action Out .

Da ethvert epos kun vedrører en bestemt type handling, har vi en speciel operatør til action$(stream) til at filtrere uønskede handlinger fra strømmen. Denne operatør er ofType()operatør.

Omskrivning af det forrige Epic ved hjælp af ofTypefår vi:

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

Hvis du vil have din episk at tillade mere end én type handling, det ofType()kan operatøren tage et vilkårligt antal argumenter som så: ofType(type1, type2, type3,...).

Introduktion til det specifikke, hvordan epics fungerer

Du tror måske, at handlingen 'PING' simpelthen kommer ind og bliver fortæret af dette epos. Det er ikke tilfældet. Der er to ting, du altid skal huske:

  1. Hver handling går altid først til reduktionsgearet
  2. Først derefter modtages denne handling af eposet

Derfor fungerer Redux-cyklen normalt som den skal.

Den action‘PING’når reduceringsanordningen først og modtages derefter af Epic og ændres derefter til en ny, action‘PONG’der sendes til reduceren.

Vi kan endda få adgang til butikens tilstand inde i en Epic, fordi en Epics andet argument er en let version af Redux Store! Se nedenunder:

const myEpic = (action$, store) =>

Vi kan bare ca ll store.getState () og få adgang til staten inde i Epics.

Operatørkæde

Mellem modtagelse af en handling og afsendelse af en ny kan vi gøre alle mulige asynkroniserede bivirkninger, som vi vil, såsom AJAX-opkald, web-stik, timere osv. Dette gøres ved hjælp af de mange operatører, der leveres af Rx.

Disse Rx-operatører giver dig mulighed for at komponere asynkrone sekvenser sammen på en deklarativ måde med alle effektivitetsfordelene ved tilbagekald, men uden ulemperne ved indlejrede tilbagekaldshåndterere, der typisk er forbundet med asynkrone systemer.

Vi får fordelene ved tilbagekald uden det berygtede 'callback-helvede'.

Se hvordan vi kan udnytte kraften fra operatører nedenfor.

En almindelig brugssag

Antag, at vi vil søge efter et ord med noget som en ordbog-API ved hjælp af tekst indtastet af brugeren i realtid. Vi har grundlæggende at gøre med lagring (i Redux-butikken) og visning af resultaterne fra API-opkaldet. Vi vil også afvise API-opkaldet, så API kaldes inden for f.eks. Et sekund, når brugeren holder op med at skrive.

Dette gøres ved hjælp af Epic- og RxJS-operatører:

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})) )

For meget at håndtere ?! Bare rolig, lad os nedbryde det.

Epikken får en strøm af handlinger alle oftype‘SEARCH’. Da brugeren kontinuerligt skriver, indeholder nyttelasten for hver indgående handling ( action.payload) den opdaterede søgestreng.

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

Vil du forbedre dine grundlæggende JavaScript? Giv disse en læsning:

JavaScript ES6-funktioner: de gode dele

ES6 tilbyder nogle seje nye funktionelle funktioner, der gør programmering i JavaScript meget mere fleksibel. Lad os tale om ... medium.freecodecamp.org En guide til JavaScript-variabel hejsning? med let og konst

N ew JavaScript-udviklere har ofte svært ved at forstå den unikke opførsel af variabel / funktion hoisting.m edium.freecodecamp.org Funktion hejsning & Hejsning Interview Spørgsmål

Dette er en del 2 til min tidligere artikel om variabel hejsning med titlen “En guide til JavaScript-variabel hejsning? med ... m edium.freecodecamp.org

Fred ✌️