-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(action): Stop passing a function to store.dipatch and stop effect… #1914
fix(action): Stop passing a function to store.dipatch and stop effect… #1914
Conversation
… that is returns a function ngrx#1906
@@ -13,12 +25,7 @@ export class ActionsSubject extends BehaviorSubject<Action> | |||
} | |||
|
|||
next(action: Action): void { | |||
if (typeof action === 'undefined') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There were no tests for this block (for the errors in the expression sections). Should I write some for it?
Preview docs changes for 216a4a5 at https://previews.ngrx.io/pr1914-216a4a5/ |
@@ -17,6 +19,7 @@ export class EffectsRunner implements OnDestroy { | |||
if (!this.effectsSubscription) { | |||
this.effectsSubscription = this.effectSources | |||
.toActions() | |||
.pipe(tap(action => throwErrorOnInvalidAction(action))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to to verify the action here. The action returned will be dispatched to the store, and will be verified there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are absolutely right. The circle closes at the place!
EffectsRunner.start() => Store.next() => ActionsSubject.next()
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its removed!
modules/store/src/actions_subject.ts
Outdated
throw new TypeError(`Actions must be objects`); | ||
} else if (typeof action === 'function') { | ||
throw new TypeError(` | ||
Actions as function are not now allowed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actions as function are not now allowed. | |
Actions as function are not allowed. |
modules/store/src/actions_subject.ts
Outdated
} else if (typeof action === 'function') { | ||
throw new TypeError(` | ||
Actions as function are not now allowed. | ||
Make sure your action is called e.g someAction()`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we make this more explicit?
Dispatch expected an object, instead it received xxx
If you're using the createAction function, make sure to invoke the function before dispatching the action. E.g. someAction should be someAction().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about this?
const typeOf = (value: any) => ({}).toString.call(value).slice(8, -1).toLowerCase();
...code...
...code...
...code...
next(action: Action): void {
if(typeOf(action) !== 'object') {
throw new TypeError(`
Dispatch expected an object, instead it received ${action}.
If you're using the createAction function, make sure to invoke the function
before dispatching the action. E.g. someAction should be someAction().`);
} else if (typeof action.type === 'undefined') {
throw new TypeError(`Actions must have a type property`);
}
super.next(action);
}
Should we test this cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its more explicit now (commited)
Preview docs changes for 55ed464 at https://previews.ngrx.io/pr1914-55ed464/ |
There should be an added check here also https://github.com/ngrx/platform/blob/master/modules/effects/src/effect_notification.ts#L33 |
Is there any place in ngrx where we can put helper methods? e.g. this: const throwErrorOnInvalidAction = action => {
if(typeOf(action) !== 'object') {
throw new TypeError(`
Dispatch expected an object, instead it received ${action}.
If you're using the createAction function, make sure to invoke the function
before dispatching the action. E.g. someAction should be someAction().`);
} else if (typeof action.type === 'undefined') {
throw new TypeError(`Actions must have a type property`);
}
} This can be used can also in https://github.com/ngrx/platform/blob/master/modules/effects/src/effect_notification.ts#L33 |
No. In this case they will just have to be separate. I don't think the function needs to exist in the public API for either library. |
@brandonroberts I hope I understood you correctly! Like this? const typeOf = action => ({}).toString.call(action).slice(8, -1).toLowerCase();
function isAction(action: any): action is Action {
return typeOf(action) === 'object' && action && action.type && typeof action.type === 'string';
} |
Preview docs changes for 2b25c95 at https://previews.ngrx.io/pr1914-2b25c95/ |
@brandonroberts its separated now (commited) |
modules/store/src/actions_subject.ts
Outdated
@@ -13,12 +19,14 @@ export class ActionsSubject extends BehaviorSubject<Action> | |||
} | |||
|
|||
next(action: Action): void { | |||
if (typeof action === 'undefined') { | |||
throw new TypeError(`Actions must be objects`); | |||
if (typeOf(action) !== 'object') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would typeof action !== 'object'
not work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a edge case when you pass accidently an array instead of object.
typeof [] // object
// this code snipped can recognize each kind of type
({}).toString.call([]).slice(8, -1).toLowerCase() // array
I just wanted a strict treatment regardless of whether we use typescript or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's true that array is type of object
as well, however:
- we are trying to catch the case where
function
is passed instead ofobject
, for example action creator is passed without calling it - even if array doesn't run into this check, it will be caught with the next if-statement
typeof action.type === 'undefined'
Also, I find the code snippet ({}).toString.call([]).slice(8, -1).toLowerCase()
to be somewhat "crafty" 😁and not very straightforward/readably. I'd rather avoid it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @alex-okrushko.
To keep it readable I'm in favor of using typeof
.
Plus we already have a compile error if we try to dispatch an array. The createAction
is only the problem here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the same thoughts (compile error) after reading what @alex-okrushko wrote. I was to strict! I have to find a balance between typescript and javascript :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
({}).toString.call([]).slice(8, -1).toLowerCase()
its removed. There is now a simple the for function.
@@ -30,8 +30,19 @@ export function reportInvalidActions( | |||
} | |||
} | |||
|
|||
const typeOf = (value: any) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally these should be structured as function declarations, e.g.
function typeOf(value: any) {...}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The typeOf
is removed. We use typeof xxx
as you suggested.
…atch-action-and-effects-return-action
Preview docs changes for c72dbde at https://previews.ngrx.io/pr1914-c72dbde/ |
@timdeschryver @alex-okrushko all code parts were adjusted according your code review suggestions. |
Preview docs changes for ca62d3a at https://previews.ngrx.io/pr1914-ca62d3a/ |
Preview docs changes for dd9131e at https://previews.ngrx.io/pr1914-dd9131e/ |
Looks good. Thanks Serkan! |
modules/store/src/actions_subject.ts
Outdated
throw new TypeError(` | ||
Dispatch expected an object, instead it received function. | ||
If you're using the createAction function, make sure to invoke the function | ||
before dispatching the action. E.g. someAction should be someAction().`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Everyone doesn't know what e.g.
means.
before dispatching the action. E.g. someAction should be someAction().`); | |
before dispatching the action. For example, someAction should be someAction().`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
modules/store/src/actions_subject.ts
Outdated
if (typeof action === 'undefined') { | ||
if (typeof action === 'function') { | ||
throw new TypeError(` | ||
Dispatch expected an object, instead it received function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dispatch expected an object, instead it received function. | |
Dispatch expected an object, instead it received a function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
…atch-action-and-effects-return-action
Preview docs changes for 11b3e8a at https://previews.ngrx.io/pr1914-11b3e8a/ |
Preview docs changes for 9619b37 at https://previews.ngrx.io/pr1914-9619b37/ |
action && | ||
action.type && | ||
typeof action.type === 'string' | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you don't need to wrap the return in brackets. This could be reverted to what it was before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, sorry, I didn't realize that! I had already wondered why prettier always jumped on when I committed something on NgRx. That doesn't happen with my private repos. I thought it was something like a hooks coming from ngrx repo.
… that is returns a function
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Closes #1906
What is the new behavior?
Does this PR introduce a breaking change?