-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Description:
Currently, ngx-statewise follows a strict unidirectional flow: Action → Updator → Effect. While this pattern ensures predictability, it limits flexibility in scenarios where we need to perform operations before state updates. This feature will add the ability to execute effects before updators, allowing for payload generation or validation before state changes, while maintaining type safety throughout the flow.
The key challenge is supporting both cases: dispatching actions with or without payloads, while ensuring all type constraints are respected. Pre-effects must either generate a payload of the exact type required by the action (when none is provided) or work with the existing payload while preserving its original type.
Tasks:
-
Design API for Pre-Updator Effects
- Extend
createEffectto accept timing parameter ({ timing: 'pre' | 'post' }) - Ensure pre-effects strictly maintain type constraints of action payloads
- Design mechanism for pre-effects to optionally cancel actions
- Extend
-
Modify Dispatch Flow
- Update
dispatchCoreto execute pre-effects before updators - Handle payload generation for actions dispatched without payload
- Implement sequential execution of multiple pre-effects if needed
- Add robust error handling for pre-effect failures
- Update
-
Ensure Type Safety
-
Create type guards to verify payload types match action definitions
-
Add compile-time checks for pre-effect return types
-
Document type constraints for developers
-
Update README with pre-effect concept and examples
-
Create code samples for both use cases (with/without payload)
-
Document best practices for maintaining type safety
-
Expected Outcome:
- Developers can define effects that run before state updates using
{ timing: 'pre' }option - Actions requiring payloads can be dispatched without a payload when a pre-effect generates one
- Type safety is maintained throughout the flow - pre-effects must return exactly the payload type expected by the action
- Pre-effects can cancel actions by returning a special value (e.g.,
null) - Existing code continues to work without modifications
Possible Implementations:
// 1. Generator mode: action dispatched without payload
// Action definition that normally expects a payload
const fetchUserActions = defineActionsGroup({
source: 'FETCH_USER',
events: {
request: payload<{ userId: string }>(),
success: payload<User>(),
failure: payload<Error>()
}
});
// Pre-effect that generates the payload
const fetchCurrentUserEffect = createEffect(
fetchUserActions.request,
async (): Promise<{ userId: string }> => {
// Get current user ID from a service
const currentUserId = await userService.getCurrentUserId();
// Return the generated payload with correct type
return { userId: currentUserId };
},
{ timing: 'pre' }
);
// Usage: dispatch without payload, pre-effect will generate the payload
await dispatchAsync(fetchUserActions.request());// 2. Transformer mode: action dispatched with payload
// Assuming LoginCredentials has { username: string, password: string }
const loginActions = defineActionsGroup({
source: 'LOGIN',
events: {
request: payload<LoginCredentials>(),
success: payload<User>(),
failure: payload<Error>()
}
});
// Pre-effect that transforms existing payload while maintaining type
const loginEffect = createEffect(
loginActions.request,
async (credentials: LoginCredentials): Promise<LoginCredentials> => {
// Transform the credentials but maintain the LoginCredentials type
return {
username: credentials.username.toLowerCase(), // normalize username
password: credentials.password
};
},
{ timing: 'pre' }
);
// Usage: dispatch with payload, pre-effect will transform the payload
await dispatchAsync(loginActions.request({ username: 'User', password: 'pass' }));Sub-issues
Metadata
Metadata
Assignees
Labels
Projects
Status