Last active
August 5, 2016 02:59
-
-
Save shuhei/7e35144510df19e42b012cf7c3a4a4fb to your computer and use it in GitHub Desktop.
An idea of side-effect-free epic like redux-saga for redux-observable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Explicit in-out of side-effect actions. | |
function epic(action$, store) { | |
const fooReq$ = action$.ofType('FOO') | |
.map(action => call('FOO_REQ', webapi.getFoo, action.payload.id)); | |
const foo$ = action$.ofType('FOO_REQ') | |
.map(foo => ({ type: 'FOO_FETCHED', payload: { foo } })); | |
return Observable.merge( | |
fooReq$, | |
foo$ | |
); | |
} | |
// Custom operator to hide side-effect actions. | |
const CALL_REQUEST = Symbol('CALL_REQUEST'); | |
const CALL_RESPONSE = Symbol('CALL_RESPONSE'); | |
const callMiddleware = store => next => action => { | |
if (action[CALL_REQUEST]) { | |
const payload = action[CALL_REQUEST].payload; | |
// TODO: Handle errors. | |
// TODO: Handle other async primitives like thunk. | |
payload[0].apply(null, payload.slice(1)) | |
.then(result => next({ | |
[CALL_RESPONSE]: { | |
id: action[CALL_REQUEST].id, | |
payload: result | |
} | |
})); | |
} else { | |
next(action); | |
} | |
}; | |
function callOperator(action$, call$, callback) { | |
return Observable.create(subscriber => { | |
const id = uuid(); | |
const source = this; | |
const responseSub = action$ | |
.filter(a => a[CALL_RESPONSE] && a[CALL_RESPONSE].id === id) | |
.map(a => a[CALL_RESPONSE].payload) | |
.subscribe(subscriber); | |
const requestSub = source.subscribe( | |
value => { | |
try { | |
call$.next({ | |
[CALL_REQUEST]: { id, payload: callback(value) } | |
}); | |
} catch (e) { | |
subscriber.error(e); | |
} | |
}, | |
err => subscriber.error(err), | |
() => subscriber.complete() | |
); | |
// https://github.com/ReactiveX/rxjs/issues/1583 | |
return new Subscription(() => { | |
[responseSub, requestSub].forEach(s => s.unsubscribe()); | |
}); | |
}); | |
} | |
Observable.prototype.call = callOperator; | |
function epic(action$, store) { | |
const call$ = new Subject(); | |
const foo$ = action$.ofType('FOO') | |
.call(action$, call$, action => [webapi.getFoo, action.payload.id]) | |
.map(foo => ({ type: 'FOO_FETCHED', payload: { foo } })); | |
return Observable.merge( | |
call$, | |
foo$ | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment