[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
I have been messing around with Typescript types and trying to come up with ways to reduce the boilerplate required by Angular and ngrx. So, I came up with an implementation of a bindActionCreator (similar to Redux's bindActionCreators) function that can be used to bind an action creator directly to the store in a single line instead of having to define a bunch of methods on the container component which are just forwarding things to this.store.dispatch. From what I tested, this seems to work pretty well, and we even get the correct types for the parameters.
function bindActionCreator<R extends ActionCreator0>(store: Store, ac: R): R;
function bindActionCreator<T1, R extends ActionCreator1<T1>>(store: Store, ac: R): R;
function bindActionCreator<T1, T2, R extends ActionCreator2<T1, T2>>(store: Store, ac: R): R;
function bindActionCreator<T1, T2, T3, R extends ActionCreator3<T1, T2, T3>>(store: Store, ac: R): R;
function bindActionCreator<T1, T2, T3, T4, R extends ActionCreator4<T1, T2, T3, T4>>(store: Store, ac: R): R;
function bindActionCreator<T1, T2, T3, T4, T5, R extends ActionCreator5<T1, T2, T3, T4, T5>>(store: Store, ac: R): R;
function bindActionCreator(store: Store<any>, actionCreator: (...args: any[]) => Action) {
return (...args: any[]): Action => {
const action = actionCreator(...args);
store.dispatch(action);
return action
}
}
Using it in a container component…
export class TodosContainerComponent {
addTodo = bindActionCreator(this.store, fromTodos.addTodo);
removeTodo = bindActionCreator(this.store, fromTodos.removeTodo);
constructor(private store: Store<State>) {}
}
This could maybe even be taken one step further with a @dispatch (or @bindToStore) decorator:
export class TodosContainerComponent {
@dispatch addTodo = fromTodos.addTodo;
@dispatch removeTodo = fromTodos.removeTodo;
constructor(private store: Store<State>) {}
}
Any ideas/opinions about this? Is it something that would be worth having in the core (maybe for the same reasons as the inclusion of createSelector?).
There is some amount of boilerplate in the Redux pattern that you're not going to avoid. Nothing is wrong with these type of utility functions, but I don't see this as something that would be in the core to reduce boilerplate. We'd rather users understand how dispatching works instead of abstracting it away. Decorators don't have access to DI without hacks, but the same general rule applies there also. Hope this helps.
I think it misses the point. The thing is that Redux was made for React (inspired by Elm et al) and there, creating containers is a matter of a couple of lines binding things together. This is possible due to the dynamic nature of React. There is also prop (input) spread which can help in some scenarios to facilitate passing things down the component tree.
With ngrx being for Angular and Angular being very static you are forced to duplicate your code at every level of your component tree and this makes people (I see it a lot) want to just have all components as containers and inject the store into each one. This in turn makes writing code and code reviews extremely painful, frustrated and long: when I say "you should use containers and presentation components" my colleagues reply with "but it is so much work, I'll jus inject the store in all of them!"
This is why I consider that anything that can reduce the amount of boilerplate for ngrx is a good thing. Redux already requires huge amounts of boilerplate in React (which can be reduced somewhat and is mostly justified) but when putting it into Angular, it just becomes unbearable for most people.
This being said, I do understand that it might not be something for the core. Just don't agree that boilerplate is inevitable and preferable to have people know about dispatch.
Maybe I'm not understanding the issue. Can you provide more examples of how you are forced to duplicate your code? I think convincing your colleagues to use better patterns is a separate issue outside of boilerplate.