-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
The @Effect()
decorator is used to annotate classes with metadata about which properties on the class are effects. At runtime @ngrx/effects reads this metadata to determine which properties on the class instance should be subscribed to. This has four disadvantages:
1. No Type Checking
This would be legal using the decorator and would result in a runtime error:
class MyEffects {
@Effect() source$ = 'not an observable';
}
Similarly, it can't type check that the items emitted from an observable are actions if dispatch
is true.
This would be legal using the decorator and would also result in a runtime error:
class MyEffects {
@Effect() source$ = observableOf('not an action');
}
2. Incompatibility with Closure
More information about this is captured in #362, but basically the @Effect()
decorator does not work out of the box with Closure's property renaming. It requires that the developers write an additional interface to declare the property names so that Closure doesn't rename them.
3. Requires a reflect-metadata polyfill
All built-in Angular decorators are removed when compiled with the AOT compiler. We can't tap into this, so the @Effect()
decorator needs the reflect-metadata polyfill even for production builds.
4. Needs defer for testing some effects
If an effect uses some other observable to start you typically need to wrap it in defer(...)
so that you have an opportunity to interact with the source service in a test:
class MyEffects {
@Effect() source$ = defer(() => {
return mySocketService.connect();
});
}
Alternative
I recommend adding a new effect
function that can be used instead of the @Effect()
decorator to address the above concerns:
class MyEffect {
source$ = effect(() => this.actions.ofType(...));
source$ = effect(() => {...}, { dispatch: false });
}
At runtime @ngrx/effects would enumerate over the properties of each registered class instance and look for any effects that were created with the effect(...)
function.
This function can be strongly typed to verify that an observable is always returned by the inner callback and that the returned stream is a stream of Action
s if dispatch
is true
. Here is the proposed type signature:
export function effect<T>(source: () => Observable<T>, options: { dispatch: false }): Observable<T>;
export function effect<T extends Action>(
source: () => Observable<T>,
options?: { dipsatch: true },
): Observable<T>;
If accepted, I would be willing to submit a PR for this feature
- Yes (Assistance is provided if you need help submitting a pull request)
- No