Claude-skill-registry asyncredux-action-status
Checks an AsyncRedux (Flutter) action's completion status using ActionStatus right after the dispatch returns. Use only when you need to know whether an action completed, whether it failed with an error, what error it produced, or how to navigate based on success or failure.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/asyncredux-action-status" ~/.claude/skills/majiayu000-claude-skill-registry-asyncredux-action-status && rm -rf "$T"
skills/data/asyncredux-action-status/SKILL.mdActionStatus in AsyncRedux
The
ActionStatus object provides information about whether an action completed successfully or encountered errors. It is returned by dispatchAndWait() and related methods.
Getting ActionStatus
Use
dispatchAndWait() to get the status after an action completes:
var status = await dispatchAndWait(MyAction());
From within an action, you can also use:
var status = await dispatchAndWait(SomeOtherAction());
ActionStatus Properties
Completion Status
: ReturnsisCompleted
if the action has finished executing (whether successful or failed)true
: ReturnsisCompletedOk
if the action finished without errors in bothtrue
andbefore()
methodsreduce()
: ReturnsisCompletedFailed
if the action encountered errors (opposite oftrue
)isCompletedOk
Error Information
: The error originally thrown byoriginalError
orbefore()
, before any modificationreduce()
: The error after processing by the action'swrappedError
methodwrapError()
Execution Tracking
These properties track which lifecycle methods have completed:
: ReturnshasFinishedMethodBefore
if thetrue
method completedbefore()
: ReturnshasFinishedMethodReduce
if thetrue
method completedreduce()
: ReturnshasFinishedMethodAfter
if thetrue
method completedafter()
Note: The execution tracking properties are primarily meant for testing and debugging. In production code, focus on
isCompletedOk and isCompletedFailed.
Common Use Cases
Conditional Navigation After Success
The most common production use is checking if an action succeeded before navigating:
// In a widget callback Future<void> _onSavePressed() async { var status = await context.dispatchAndWait(SaveFormAction()); if (status.isCompletedOk) { Navigator.pop(context); } }
Another example with push navigation:
Future<void> _onLoginPressed() async { var status = await context.dispatchAndWait(LoginAction( email: emailController.text, password: passwordController.text, )); if (status.isCompletedOk) { Navigator.pushReplacementNamed(context, '/home'); } // If failed, the error will be shown via UserExceptionDialog }
Testing Action Errors
Use ActionStatus to verify that actions throw expected errors:
test('MyAction fails with invalid input', () async { var store = Store<AppState>(initialState: AppState.initial()); var status = await store.dispatchAndWait(MyAction(value: -1)); expect(status.isCompletedFailed, isTrue); expect(status.wrappedError, isA<UserException>()); expect((status.wrappedError as UserException).msg, "Value must be positive"); });
Testing Action Success
test('SaveAction completes successfully', () async { var store = Store<AppState>(initialState: AppState.initial()); var status = await store.dispatchAndWait(SaveAction(data: validData)); expect(status.isCompletedOk, isTrue); expect(store.state.saved, isTrue); });
Checking Original vs Wrapped Error
When your action uses
wrapError() to transform errors, you can inspect both:
class MyAction extends AppAction { @override Future<AppState?> reduce() async { throw Exception('Network error'); } @override Object? wrapError(Object error, StackTrace stackTrace) { return UserException('Could not save. Please try again.'); } } // In test: var status = await store.dispatchAndWait(MyAction()); expect(status.originalError, isA<Exception>()); // The original Exception expect(status.wrappedError, isA<UserException>()); // The wrapped UserException
Action Lifecycle and Status
The action lifecycle runs in this order:
- Runs first, can be used for preconditionsbefore()
- Runs second (only ifreduce()
succeeded)before()
- Runs last, always executes (like a finally block)after()
The
isCompletedOk property is true only if both before() and reduce() completed without errors. Note that errors in after() do not affect isCompletedOk.
If
before() throws an error, reduce() will not run, but after() will still execute.
Best Practices
-
Use state changes for UI updates: In production, prefer checking state changes rather than action status. Reserve ActionStatus for cases where you need to perform side effects (like navigation) based on success/failure.
-
Use
for navigation: The common pattern is to navigate only after an action succeeds:isCompletedOkif (status.isCompletedOk) Navigator.pop(context); -
Use
in tests: When testing error handling, checkwrappedError
to see what the user will actually see (afterwrappedError
processing).wrapError() -
Use
for debugging: When you need to see the underlying error before any transformation, useoriginalError
.originalError
References
URLs from the documentation:
- https://asyncredux.com/flutter/advanced-actions/action-status
- https://asyncredux.com/flutter/basics/dispatching-actions
- https://asyncredux.com/flutter/basics/failed-actions
- https://asyncredux.com/flutter/advanced-actions/errors-thrown-by-actions
- https://asyncredux.com/flutter/advanced-actions/before-and-after-the-reducer
- https://asyncredux.com/flutter/advanced-actions/redux-action
- https://asyncredux.com/flutter/miscellaneous/navigation
- https://asyncredux.com/flutter/testing/store-tester
- https://asyncredux.com/flutter/testing/dispatch-wait-and-expect
- https://asyncredux.com/flutter/testing/testing-user-exceptions