My use case is:
I'm trying to use Observables within my Redux app with redux-observable. If you can think of another way of making the above user flow work I would be happy to hear how.
NB. Im using rxjs V5
export const fetchAssetListEpic = (action$, store) => {
return action$.ofType('FETCH_ASSET_LIST')
.switchMap( action => {
const options = {
crossDomain: true,
withCredentials: true,
url: uriGenerator('assetList', action.payload)
};
return ajax(options);
})
.map(fetchAssetListSuccess)
.retryWhen(handleError)
.catch(redirectToSignIn);
};
function handleError(err) {
return (err.status === 401) ?
/* Authenticate here [Step 2] */
/* Send new JWT to API [Step 3] */
/* If successful make original request again [Step 4] */
:
Observable.throw(err);
}
function redirectToSignIn() {
/*I will redirect here*/
}
So far I able to complete steps 1, 2 and 3 but not too sure of a way to add step 4. I may be completely off the mark but any help would be great!
Well one thing you probably won't want to do is allow the error to make it to the top level stream. Even if you do a catch
you have effectively killed the top level stream. So unless your redirect is doing a hard redirect instead of a a soft one via something like react-router, you won't be able to use this epic any more.
Thus I would say that you want most of the logic to be encapsulated within the switchMap
:
function withAuthorizedFlow(source) {
return source
.map(fetchAssetListSuccess)
// retryWhen takes a callback which accepts an Observable of errors
// emitting a next causes a retry, while an error or complete will
// stop retrying
.retryWhen(e => e.flatMap(err =>
Observable.if(
// Returns the first stream if true, second if false
() => err.status === 401,
reauthenticate, // A stream that will emit once authenticated
Observable.throw(err) // Rethrow the error
))
)
.catch(redirectToSignIn);
}
/** Within the epic **/
.switchMap(({payload}) => {
const options = {
crossDomain: true,
withCredentials: true,
url: uriGenerator('assetList', payload)
};
// Invoke the ajax request
return ajax(options)
// Attach a custom pipeline here
// Not strictly necessary but it keeps this method clean looking.
.let(withAuthorizedFlow);
})
The use of let
above is completely optional, I threw it in to clean up the function. Essentially though you want to contain the error to the inner stream so that it can't halt the outer one. I am not sure which ajax
library you are using but you should also confirm that it will in fact return a cold Observable
otherwise you will need to wrap it in a defer
block to in order for the retryWhen
to work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With