Closed ArielGueta closed 7 years ago
I was thinking about something similar the other day, and also looking into:
I do like the idea of simplifying HTTP requests - I have been noticing a number of 'api' services that are doing nothing but the request - and no transformation of inputs/outputs because they are being handled in other parts of the application.
Instead of:
and repeat for each API service I have, I do like the idea of being able to
dispatch({type: 'API_REQUEST', payload: {
url: '',
body: ''
method: 'get'
map: (result)=> { map to what my app needs, } ,
actions: 'A_SUCCESS', 'A_ERROR' // actions out to this type?
})
One thing I'd be curious to see how it worked out, is calls that might need to make multiple requests (maybe some sync, some parallel).
I do think that Epics could coordinate this also, but 'old way', might be something like:
let req1 = someHttp1.get()
let req2 = someHtp2.get()
Observable
.forkJoin(req1, req2)
.switchMap(([res1,res2])=> someHttp3.get(res1.x,res1.y))
If you had a super-generic HTTP-middleware - as long as the 'success' types were distinct, could possibly write an epic like
fullObj = (action$) => {
Observable
.forkJoin(action.ofType('SUCCESS_A'),
action.ofType('SUCCESS_B'))
.map(([res1,res2])=>Observable.of(
{type: 'DO_3rd Call', url: `some/url/${res1.payload.id}/${res2.payload.id}`)
}
Would need to play around with the idea a bit more - for things that only need one API call to be fulfilled, makes sense - or, if a few API calls that don't rely on each-other - just the results of all of them.
Where my head starts to bend a bit - is when chained calls are needed, but I do think with a bit more thought/experimentation it could work out
Thanks for the answer. I also don't understand what is the benefit of inject the action creator with the DI.
They are just plain objects, why not import them like any other ES2015 module? ( I saw this is what the ngrx/store guys do )
You don't need to use DI to get them, although a common pattern I was seeing in some projects is people wanting access to their other angular services in their action creators (mainly things dealing with Http) - and not wanting to put that logic into their components.
That said, the more I'm using something like redux-observable, the less need I have for access to DI in my action creators, and starting to go back to the plain function exports / plain JSON objects.
There is nothing saying you need to use @angular/http
either - could use fetch/etc if you wanted.
I just saw in the repo examples that this is how they are doing this even without injecting other things like CounterActions. https://github.com/angular-redux/store/blob/master/docs/intro-tutorial.md#actions
Theres a few ways you can handle action creators, and I've changed my opinion/preference a few times. There is also a bit of the 'react-redux' mindset/way, vs 'what feels natural to most Angular devs'.
What initially led me to going with ActionServices
for awhile - as injectable things, not just imports, was dealing with async and wanting access to other providers/services.
Once they became injectable services - hey, can inject ngRedux, and have access to getState/dispatch from there also, and can drop redux-thunk
.
@Injectable()
export class SomeAction {
constructor(private someApi: SomeAPI, private ngRedux: NgRedux<State>) { }
doSomething() {
this.someApi
.getStuff()
.subscribe(result => this.ngRedux.dispatch({ type: 'GOT_STUFF', payload: result });
}
}
@Component({/*stuff*/)
export class SomeComponent {
@select(['stuff']) stuff$;
constructor(private someAction: SomeAction) { }
doSomething() {
this.someAction.doSomething();
// instead of injecting redux, and ngRedux.dispatch(doSomething())
}
}
this has a few pro's and con's - components don't need to worry about binding dispatch to actions, however it starts to encourage very fat action creators, since your not doing just a plain JSON object to return - slightly more complex to test. Could refactor out the thing that forms up the action into it's own function and test that.
For awhile I was a little bit hesitant about things like redux-saga or redux-observable, and felt like dealing with async there added too much indirection - and for smaller projects, think that may still hold true.
That said, I've become a big fan of redux-observable - and going towards that for coordinating streams, dealing with async/http, etc - and having my action creators be injectable services has less appeal, and favouring "just return an object, and don't bother with DI/etc". Although, this is preference and not prescriptive - and why there isn't anything about angular-redux that forces one way or the other.
In the end - it's all just middleware (epics are just middleware also), and being able to pick the ones that best fits your needs is a large part of why I like redux, and being able to leverage the existing redux ecosystem is a large part of the motivation behind angular-redux over alternatives like roll-your-own-redux, or ngrx/store.
Great answer, thanks. My problem with redux is that there are so many ways to do the same thing. Some people like you take this as an advantage, but I think it's just confusing and adding more learning curve. I wish there were one particular style guide to "how to redux."
Closing as an issue discussion. As for a global guide, yeah... :) Things change real fast though as you know.
Hey,
I saw in your recommendations and your flow that you are using redux-epics. I am wondering if there is a benefit to creating epic per "action/feature" compared to global HTTP epic middleware that will handle the requests?
Thanks.