Is there a way to streamline action definitions to eliminate repetitive code?
Challenge
When working with Ngrx, actions are constantly needed. Typically, each action consists of a "Do", "Success", and "Failure" variation. These actions often share the same scope (e.g. "[Authentication]") and label (e.g. "Login user").
For example, creating authentication actions involves significant repetition:
export const loginUser = createAction(
'[Authentication] DO: Login user',
props<{
username: string;
password: string;
}
);
export const loginUserSuccess = createAction(
'[Authentication] SUCCESS: Login user',
props<{
token: string;
}
);
export const loginUserFailure = createAction(
'[Authentication] FAILURE: Login user',
props<{
error: string;
}
);
The redundancy includes:
- three instances of "[Authentication]" for the same scope
- three occurrences of "Login user" for the same type of action
- repeating the "DO", "SUCCESS", "FAILURE" components in every action
Is there a method to simplify action creation with less duplication?
My Current Approach
Refer to my response below.
I developed a package ngrx-simple
aiming to streamline ngrx development. It introduces a class SimpleActions
that facilitates action grouping and reduces redundancy:
https://github.com/julianpoemp/ngrx-simple
Previous Solution
(the new package code is preferred)
The only simplification I discovered involved creating a set of actions within the same scope and encapsulating all actions of a similar type within an object:
store.functions.ts
import {createAction, props} from '@ngrx/store';
export function createDoActionType(scope: string, label: string) {
return `[${scope}] DO: ${label}`;
}
export function createSuccessActionType(scope: string, label: string) {
return `[${scope}] SUCCESS: ${label}`;
}
export function createErrorAction(scope: string, label: string) {
return createAction(
`[${scope}] FAILURE: ${label}`,
props<ErrorProps>()
);
}
export interface ErrorProps {
error: string;
}
authentication.actions.ts
export class AuthenticationActions {
private static scope = 'Authentication';
static loginUser = ((scope: string, label: string) => ({
do: createAction(
createDoActionType(scope, label),
props<{
userEmail: string;
password: string;
}>()
),
success: createAction(
createSuccessActionType(scope, label),
props<{
userID: number;
token: string;
}>()
),
failure: createErrorAction(scope, label)
}))(AuthenticationActions.scope, 'Login user');
}
This workaround, while saving lines of code, is not considered optimal...