-
-
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
RFC: Add action creator factory to Store #1480
Comments
TIP: There is a project called typescript-fsa (https://github.com/aikoven/typescript-fsa) which does something similar. |
What about Martin's proposal? Also the syntax is very good for schematics to add actions. |
Could you elaborate more on the requirements?
|
The requirements are only dealing with action creators. I think something similar to what @MichaelWarneke mentioned is what I'd like to see. Reducer factories and AoT don't mix, as you end up having to wrap it in another function. At a minimum there is a generic |
Something like? // RFC: Add action creator factory to Store
// https://github.com/ngrx/platform/issues/1480
//
//
// tslint:disable:max-classes-per-file
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects';
// import { Action } from '@ngrx/store';
import { tap } from 'rxjs/operators';
/* ngrx
export interface Action {
type: string;
}
*/
/**************************************************
* rex-tils
* https://github.com/Hotell/rex-tils
*************************************************/
export type AnyFunction = (...args: any[]) => any
// tslint:disable-next-line:interface-over-type-literal
export type StringMap<T> = { [key: string]: T }
export type Action<T extends string = string, P = void> = P extends void
? Readonly<{ type: T }>
: Readonly<{ type: T; payload: P }>
export function createAction<T extends string>(type: T): Action<T>
export function createAction<T extends string, P>(
type: T,
payload: P
): Action<T, P>
export function createAction<T extends string, P>(type: T, payload?: P) {
const action = payload === undefined ? { type } : { type, payload }
// return IS_DEV ? Object.freeze(action) : action
return action
}
export type ActionsUnion<A extends StringMap<AnyFunction>> = ReturnType<
A[keyof A]
>
/**************************************************
* Actions
*************************************************/
export const enum ActionTypes {
Login = '[Login Page] Login',
Logout = '[Login Page] Logout',
}
const RexActions = {
login: (username: string, password: string) =>
createAction(ActionTypes.Login, { username, password }),
logout: () => createAction(ActionTypes.Logout),
};
type RexActions = ActionsUnion<typeof RexActions>;
const action1 = RexActions.login('UserName', 'Password');
const action2 = RexActions.logout();
/**************************************************
* Effects
*************************************************/
@Injectable()
export class AuthEffects {
@Effect({ dispatch: false })
authSignInSuccess$ = this.actions$.pipe(
ofType(ActionTypes.Login),
tap((loginAction) => {
console.log(loginAction.payload.username);
}));
@Effect({ dispatch: false })
logout$ = this.actions$.pipe(
ofType(ActionTypes.Logout),
tap((logoutAction) => {
console.log(logoutAction.type);
})
);
constructor(private actions$: Actions<RexActions>) {}
}
/**************************************************
* Reducer
*************************************************/
interface AuthState
extends Readonly<{
hasChecked: boolean;
}> {}
const initialState: AuthState = {
hasChecked: false,
};
function authReducer(state = initialState, action: RexActions): AuthState {
switch (action.type) {
case ActionTypes.Login:
const a = action.payload.username;
return { ...initialState, hasChecked: true };
default:
return state;
}
} |
I would like to have an API sort of like this //
// Creating actions
//
const someActionFollowingPayloadScheme = actionCreator.withPayload<string>("MyAction");
someActionFollowingPayloadScheme("bla"); // --> {type: "MyAction", payload: "bla"}
const myAction = actionCreator<{bla: boolean; blubb: string}>("MyAction");
myAction({bla: true, blubb: "blubb"}); // --> {type: "MyAction", bla: true, blubb: "blubb"}
const someActionWithPayloadTransformation = actionCreator("MyAction", (a: boolean, b: string) => ({c: !a, d: b}));
someActionWithPayloadTransformation(true, "bla"); // --> {type: "MyAction", c: false, d: "bla"};
// Above also supported on the withPayload-actionCreator
//
// Match Util
//
if (myAction.match(someOtherAction)) {
// someOtherAction inferred as type of myAction
}
effect$ = this.actions$.pipe(
filter(myAction.match),
map(action => {
// inferred as type of myAction
)
); Maybe this can be combined with the ideas above. |
@dummdidumm I don't see that as part of this implementation. Something similar to what @tja4472 posted is where we want to start. |
Well, at least some form of match-utility would be nice, it would make the action's type inference self-contained and not dependent on |
We encourage users to create many unique actions to describe application events. We should make creating and using these actions easier without abstractions.
Goals:
This would also make mapping within observable operators easier if no changes are required for the payload.
Describe any alternatives/workarounds you're currently using
Using action classes
Other information:
Similar request #379, but this is focused on creating actions and not dispatching and decorators.
The text was updated successfully, but these errors were encountered: