I det voksende økosystemet med rike og kraftige nett- og mobilapper kan det administreres mer og mer tilstand, som nåværende bruker, liste over varer lastet inn, lastetilstand, feil og mye mer. Redux er en løsning på dette problemet ved å holde staten i et globalt objekt.
En av begrensningene med Redux er at den ikke støtter asynkron oppførsel ut av esken. En løsning på dette er redux-observable
, som er basert på RxJS, et kraftig bibliotek for reaktiv programmering i JavaScript. RxJS er en implementering av ReactiveX, et API for reaktiv programmering som stammer fra Microsoft. ReactiveX kombinerer noen av de kraftigste funksjonene i det reaktive paradigmet, funksjonell programmering, observatørmønsteret og iteratormønsteret.
I denne veiledningen lærer vi om Redux og bruken av den med React. Vi vil også utforske reaktiv programmering ved bruk av RxJS og hvordan det kan gjøre kjedelig og kompleks asynkron arbeid veldig enkel.
Til slutt lærer vi redux-observerbar, et bibliotek som utnytter RxJS til å utføre asynkront arbeid, og vil deretter bygge en applikasjon i React Native ved hjelp av Redux og redux-observerbar.
Som det beskriver seg selv på GitHub, er Redux 'en forutsigbar tilstandsbeholder for JavaScript-apper.' Det gir JavaScript-appene dine en global tilstand, og holder tilstand og handlinger borte fra React-komponenter.
I et typisk React-program uten Redux, må vi overføre data fra rotnoden til barn via egenskaper, eller props
. Denne datastrømmen er håndterbar for små applikasjoner, men kan bli veldig kompleks når applikasjonen din vokser. Redux lar oss ha komponenter uavhengige av hverandre, og dermed kan vi bruke den som en enkelt kilde til sannhet.
Redux kan brukes i React ved hjelp av react-redux
, som gir bindinger for React-komponenter til å lese data fra Redux og sende handlinger for å oppdatere Redux-tilstanden.
Redux kan beskrives som tre enkle prinsipper:
De stat av hele applikasjonen din er lagret i et enkelt objekt. Dette objektet i Redux holdes av en butikk . Det bør være en enkelt butikk i en hvilken som helst Redux-app.
» console.log(store.getState()) « { user: {...}, todos: {...} }
For å lese data fra Redux i React-komponenten bruker vi connect
funksjon fra react-redux
. connect
tar fire argumenter, som alle er valgfrie. Foreløpig vil vi fokusere på den første, kalt mapStateToProps
.
/* UserTile.js */ import { connect } from 'react-redux'; class UserTile extends React.Component { render() { return { this.props.user.name }
} } function mapStateToProps(state) { return { user: state.user } } export default connect(mapStateToProps)(UserTile)
I eksemplet ovenfor, mapStateToProps
mottar den globale Redux-tilstanden som sitt første argument og returnerer et objekt som vil bli slått sammen med rekvisittene som er overført til sin overordnede komponent.
Redux-tilstand er skrivebeskyttet for React-komponenter, og den eneste måten å endre tilstanden på er å sende ut en handling . En handling er et vanlig objekt som representerer en intensjon om å endre tilstanden. Hvert handlingsobjekt må ha et type
felt, og verdien må være en streng. Annet enn det, er innholdet i handlingen helt opp til deg, men de fleste apper følger en fluks-standard-handling format, som begrenser strukturen til en handling til bare fire taster:
type
Enhver strengidentifikator for en handling. Hver handling må ha en unik handling.payload
Valgfri data for enhver handling. Det kan være når som helst og inneholder informasjon om handlingen.error
Enhver valgfri boolsk eiendom satt til sant hvis handlingen representerer en feil. Dette er analogt med et avvist Promise. string
identifikator for en handling. Hver handling må ha en unik handling. Etter konvensjon, når error
er true
, payload
skal være et feilobjekt.meta
Meta kan være hvilken som helst verdi. Den er ment for ekstra informasjon som ikke er en del av nyttelasten.Her er to eksempler på handlinger:
store.dispatch({ type: 'GET_USER', payload: '21', }); store.dispatch({ type: 'GET_USER_SUCCESS', payload: { user: { id: '21', name: 'Foo' } } });
Den globale Redux-staten endres ved hjelp av rene funksjoner som kalles reduksjonsmidler. En redusering tar forrige tilstand og handling og returnerer neste tilstand. Reduksjonsapparatet oppretter et nytt statsobjekt i stedet for å mutere det eksisterende. Avhengig av appstørrelse, kan en Redux-butikk ha en enkelt redusering eller flere reduksjoner.
/* store.js */ import { combineReducers, createStore } from 'redux' function user(state = {}, action) { switch (action.type) { case 'GET_USER_SUCCESS': return action.payload.user default: return state } } function todos(state = [], action) { switch (action.type) { case 'ADD_TODO_SUCCESS': return [ ...state, { id: uuid(), // a random uuid generator function text: action.text, completed: false } ] case 'COMPLETE_TODO_SUCCESS': return state.map(todo => { if (todo.id === action.id) { return { ...todo, completed: true } } return todo }) default: return state } } const rootReducer = combineReducers({ user, todos }) const store = createStore(rootReducer)
I likhet med å lese fra staten, kan vi bruke et connect
funksjon for å sende handlinger.
/* UserProfile.js */ class Profile extends React.Component { handleSave(user) { this.props.updateUser(user); } } function mapDispatchToProps(dispatch) { return ({ updateUser: (user) => dispatch({ type: 'GET_USER_SUCCESS', user, }), }) } export default connect(mapStateToProps, mapDispatchToProps)(Profile);
Reaktiv programmering er et deklarativt programmeringsparadigme som omhandler datastrømmen i “ bekker ”Og med forplantning og endringer. RxJS, et bibliotek for reaktiv programmering i JavaScript, har et konsept av observerbar , som er datastrømmer som en observatør kan abonnere til, og denne observatøren leveres data over tid.
En observatør av en observerbar er et objekt med tre funksjoner: next
, error
, og complete
. Alle disse funksjonene er valgfrie.
observable.subscribe({ next: value => console.log(`Value is ${value}`), error: err => console.log(err), complete: () => console.log(`Completed`), })
.subscribe
funksjon kan også ha tre funksjoner i stedet for et objekt.
observable.subscribe( value => console.log(`Value is ${value}`), err => console.log(err), () => console.log(`Completed`) )
Vi kan lage et nytt observerbart ved å lage et objekt til en observable
, passere inn en funksjon som mottar en abonnent, altså observatør. Abonnenten har tre metoder: next
, error
, og complete
. Abonnenten kan ringe neste med en verdi så mange ganger som nødvendig, og complete
eller error
til slutt. Etter å ha ringt complete
eller error
, vil den observerbare ikke skyve noen verdi nedover strømmen.
import { Observable } from 'rxjs' const observable$ = new Observable(function subscribe(subscriber) { const intervalId = setInterval(() => { subscriber.next('hi'); subscriber.complete() clearInterval(intervalId); }, 1000); }); observable$.subscribe( value => console.log(`Value is ${value}`), err => console.log(err) )
Eksemplet ovenfor vil skrive ut Value is hi
etter 1000 millisekunder.
Å lage en observerbar manuelt hver gang kan bli verbose og kjedelig. Derfor har RxJS mange funksjoner for å skape en observerbar. Noen av de mest brukte er of
, from
og ajax
.
of
tar en sekvens av verdier og konverterer den til en strøm:
import { of } from 'rxjs' of(1, 2, 3, 'Hello', 'World').subscribe(value => console.log(value)) // 1 2 3 Hello World
from
konverterer nesten hva som helst til en strøm av verdier:
import { from } from 'rxjs' from([1, 2, 3]).subscribe(console.log) // 1 2 3 from(new Promise.resolve('Hello World')).subscribe(console.log) // 'Hello World' from(fibonacciGenerator).subscribe(console.log) // 1 1 2 3 5 8 13 21 ...
ajax
tar en streng URL eller oppretter en observerbar som sender en HTTP-forespørsel. ajax
har en funksjon ajax.getJSON
, som bare returnerer det nestede svarobjektet fra AJAX-anrop uten andre egenskaper som returneres av ajax()
:
import { ajax } from 'rxjs/ajax' ajax('https://jsonplaceholder.typicode.com/todos/1').subscribe(console.log) // {request, response: {userId, id, title, completed}, responseType, status} ajax.getJSON('https://jsonplaceholder.typicode.com/todos/1').subscribe(console.log) // {userId, id, title, completed} ajax({ url, method, headers, body }).subscribe(console.log) // {...}
Det er mange flere måter å gjøre en observerbar (du kan se hele listen her ).
Operatører er et reelt kraftverk for RxJS, som har en operatør for nesten alt du trenger. Siden RxJS 6 er operatorer ikke metoder på det observerbare objektet, men rene funksjoner som brukes på det observerbare ved hjelp av et .pipe
metode.
map
tar en enkelt argumentfunksjon og bruker en projeksjon på hvert element i strømmen:
import { of } from 'rxjs' import { map } from 'rxjs/operators' of(1, 2, 3, 4, 5).pipe( map(i=> i * 2) ).subscribe(console.log) // 2, 4, 6, 8, 10
filter
tar et enkelt argument og fjerner verdier fra strømmen som returnerer false for den gitte funksjonen:
import { of } from 'rxjs' import { map, filter } from 'rxjs/operators' of(1, 2, 3, 4, 5).pipe( map(i => i * i), filter(i => i % 2 === 0) ).subscribe(console.log) // 4, 16
flatMap
operatøren tar en funksjon som kartlegger hvert element i dampen til en annen strøm og flater alle verdiene til disse strømningene:
import { of } from 'rxjs' import { ajax } from 'rxjs/ajax' import { flatMap } from 'rxjs/operators' of(1, 2, 3).pipe( flatMap(page => ajax.toJSON(`https://example.com/blog?size=2&page=${page}`)), ).subscribe(console.log) // [ { blog 1 }, { blog 2 }, { blog 3 }, { blog 4 }, { blog 5 }, { blog 6 } ]
merge
fletter varer fra to strømmer i rekkefølgen de ankommer:
import { interval, merge } from 'rxjs' import { pipe, take, mapTo } from 'rxjs/operators' merge( interval(150).pipe(take(5), mapTo('A')), interval(250).pipe(take(5), mapTo('B')) ).subscribe(console.log) // A B A A B A A B B B
En full liste over operatører er tilgjengelig her .
Etter design er alle handlinger i Redux synkrone. Redux-observerbar er en mellomvare for Redux som bruker observerbare strømmer for å utføre asynkront arbeid og deretter sende en annen handling i Redux med resultatet av det asynkrone arbeidet.
Redux-observerbar er basert på ideen om Epics . Et epos er en funksjon som tar en strøm av handlinger, og eventuelt en tilstandsstrøm og returnerer en strøm av handlinger.
function (action $: Observable, state $: StateObservable): Observable;
Etter konvensjon ender hver variabel som er en strøm (_aka _observable
) med en $
. Før vi kan bruke redux-observerbar, må vi legge den til som mellomvare i butikken vår. Siden eposer er strømmer av observerbare og hver handling som går ut av denne dampen føres tilbake i strømmen, vil retur av samme handling resultere i en uendelig løkke.
const epic = action$ => action$.pipe( filter(action => action.type === 'FOO'), mapTo({ type: 'BAR' }) // not changing the type of action returned // will also result in an infinite loop ) // or import { ofType } from 'redux-observable' const epic = action$ => action$.pipe( ofType('FOO'), mapTo({ type: BAZ' }) )
Tenk på denne reaktive arkitekturen som et rørsystem hvor hvert rørs utgang strømmer tilbake til hvert rør, inkludert seg selv, og også til Reduxs reduksjonsgir. Det er filtrene på toppen av disse rørene som bestemmer hva som går inn og hva som er blokkert.
La oss se hvordan et Ping-Pong-epos ville fungere. Det tar en ping, sender den til serveren - og etter at forespørselen er fullført - sender den en pong tilbake til appen.
const pingEpic = action$ => action$.pipe( ofType('PING'), flatMap(action => ajax('https://example.com/pinger')), mapTo({ type: 'PONG' }) ) Now, we are going to update our original todo store by adding epics and retrieving users. import { combineReducers, createStore } from 'redux' import { ofType, combineEpics, createEpicMiddleware } from 'redux-observable'; import { map, flatMap } from 'rxjs/operators' import { ajax } from 'rxjs/ajax' // ... /* user and todos reducers defined as above */ const rootReducer = combineReducers({ user, todos }) const epicMiddleware = createEpicMiddleware(); const userEpic = action$ => action$.pipe( ofType('GET_USER'), flatMap(() => ajax.getJSON('https://foo.bar.com/get-user')), map(user => ({ type: 'GET_USER_SUCCESS', payload: user })) ) const addTodoEpic = action$ => action$.pipe( ofType('ADD_TODO'), flatMap(action => ajax({ url: 'https://foo.bar.com/add-todo', method: 'POST', body: { text: action.payload } })), map(data => data.response), map(todo => ({ type: 'ADD_TODO_SUCCESS', payload: todo })) ) const completeTodoEpic = action$ => action$.pipe( ofType('COMPLETE_TODO'), flatMap(action => ajax({ url: 'https://foo.bar.com/complete-todo', method: 'POST', body: { id: action.payload } })), map(data => data.response), map(todo => ({ type: 'COMPLEE_TODO_SUCCESS', payload: todo })) ) const rootEpic = combineEpics(userEpic, addTodoEpic, completeTodoEpic) const store = createStore(rootReducer, applyMiddleware(epicMiddleware)) epicMiddleware.run(rootEpic);
_Viktig: Epics er akkurat som alle andre observerbare strømmer i RxJS. De kan havne i en fullstendig eller feiltilstand. Etter denne tilstanden slutter epikken - og appen din - å fungere. Så du må fange alle potensielle feil i dampen. Du kan bruke __catchError__
operatør for dette. Mer informasjon: Feilhåndtering i redux-observerbar .
Med noe brukergrensesnitt lagt til, ser en (minimal) demo-app ut slik:
Vi lærte hva reaktive apper er. Vi lærte også om Redux , RxJS og redux-observerbar, og til og med opprettet en reaktiv Todo-app i Expo med React Native. Til Reagere og Reager Native utviklere, tilbyr de nåværende trendene noen veldig kraftige alternativer for statsadministrasjon.
Nok en gang er kildekoden for denne appen på GitHub . Del gjerne tankene dine om statsforvaltning for reaktive apper i kommentarene nedenfor.
Redux er en forutsigbar, global statlig container for JavaScript-apper. Det trekker ut statsledelsen og forandringslogikken bort fra applikasjonens React-komponenter.
Reaktiv programmering er et deklarativt programmeringsparadigme som omhandler data som en strøm og forplantning av tilstandsendring gjennom den strømmen.
ReactiveX er et API for asynkron programmering med observerbare strømmer. Den kombinerer de beste ideene fra Observer-mønsteret, Iterator-mønsteret og funksjonell programmering.
RxJS er et implementeringsbibliotek for ReactiveX API i JavaScript. Den utnytter den dynamiske naturen til JavaScript sammen med et omfattende og omfattende ReactiveX API.
Erik Meijer, datavitenskapsmann i Microsoft, opprettet ReactiveX API og implementeringen av den i .NET. RxJS ble opprettet som en port på Rx.NET av forskjellige ingeniører.
RxJS gir et stort API for å håndtere asynkrone oppgaver med funksjonell programmering, som letter bruken av handlinger som prøv på nytt, struping, eksponensiell tilbakefall, caching, feilhåndtering og mer.
redux-observerbar er et bibliotek for håndtering av asynkrone oppgaver i Redux. Det fungerer ved å fange en utsendt handling fra Redux og gjøre noe asynkront arbeid med den. Etter at arbeidet er fullført, legger 'redux-observerbar' til svaret i Redux-tilstandsbeholderen ved å sende en annen handling.
RxJS er en implementering av ReactiveX og kan brukes med hvilken som helst statsadministrasjon eller UI-bibliotek, eller til og med med vanilje JavaScript. Det gir et kraftig API for å administrere asynkroniseringsarbeid i form av observerbare strømmer. Disse strømmer kan kobles til et hvilket som helst annet bibliotek eller brukes direkte. Utenfor JavaScript kan du finne implementeringer av ReactiveX for vanlige språk på reactivex.io.