asyncredux-debugging
Debug AsyncRedux applications effectively. Covers printing state with store.state, checking actionsInProgress(), using ConsoleActionObserver, StateObserver for state change tracking, and tracking dispatchCount/reduceCount.
Best use case
asyncredux-debugging is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Debug AsyncRedux applications effectively. Covers printing state with store.state, checking actionsInProgress(), using ConsoleActionObserver, StateObserver for state change tracking, and tracking dispatchCount/reduceCount.
Teams using asyncredux-debugging should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/asyncredux-debugging/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How asyncredux-debugging Compares
| Feature / Agent | asyncredux-debugging | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Debug AsyncRedux applications effectively. Covers printing state with store.state, checking actionsInProgress(), using ConsoleActionObserver, StateObserver for state change tracking, and tracking dispatchCount/reduceCount.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Debugging AsyncRedux Applications
AsyncRedux provides several tools for debugging and monitoring your application's state, actions, and behavior during development.
## Inspecting Store State
Access the current state directly from the store:
```dart
// Direct state access
print(store.state);
// Access specific parts
print(store.state.user.name);
print(store.state.cart.items);
```
## Tracking Actions in Progress
Use `actionsInProgress()` to see which actions are currently being processed:
```dart
// Returns an unmodifiable Set of actions currently running
Set<ReduxAction<AppState>> inProgress = store.actionsInProgress();
// Check if any actions are running
if (inProgress.isEmpty) {
print('No actions in progress');
} else {
for (var action in inProgress) {
print('Running: ${action.runtimeType}');
}
}
// Get a copy of actions in progress
Set<ReduxAction<AppState>> copy = store.copyActionsInProgress();
// Check if specific actions match
bool matches = store.actionsInProgressEqualTo(expectedSet);
```
## Dispatch and Reduce Counts
Track how many actions have been dispatched and how many state reductions have occurred:
```dart
// Total actions dispatched since store creation
print('Dispatch count: ${store.dispatchCount}');
// Total state reductions performed
print('Reduce count: ${store.reduceCount}');
```
These counters are useful for:
- Verifying actions dispatched during tests
- Detecting unexpected dispatches
- Performance monitoring
## Console Action Observer
The built-in `ConsoleActionObserver` prints dispatched actions to the console with color formatting:
```dart
var store = Store<AppState>(
initialState: AppState.initialState(),
// Only enable in debug mode
actionObservers: kReleaseMode ? null : [ConsoleActionObserver()],
);
```
Console output example:
```
I/flutter (15304): | Action MyAction
I/flutter (15304): | Action LoadUserAction(user32)
```
Actions appear in yellow (default) or green (for `WaitAction` and `NavigateAction`).
### Customizing Action Output
Override `toString()` in your actions to display additional information:
```dart
class LoginAction extends AppAction {
final String username;
LoginAction(this.username);
@override
Future<AppState?> reduce() async {
// ...
}
@override
String toString() => 'LoginAction(username: $username)';
}
```
### Custom Color Scheme
Customize the color scheme by modifying the static `color` callback:
```dart
ConsoleActionObserver.color = (action) {
if (action is ErrorAction) return ConsoleActionObserver.red;
if (action is NetworkAction) return ConsoleActionObserver.blue;
return ConsoleActionObserver.yellow;
};
```
Available colors: `white`, `red`, `blue`, `yellow`, `green`, `grey`, `dark`.
## StateObserver for State Change Logging
Create a `StateObserver` to log state changes:
```dart
class DebugStateObserver implements StateObserver<AppState> {
@override
void observe(
ReduxAction<AppState> action,
AppState prevState,
AppState newState,
Object? error,
int dispatchCount,
) {
final changed = !identical(prevState, newState);
print('--- Action #$dispatchCount: ${action.runtimeType} ---');
print('State changed: $changed');
if (changed) {
// Log specific state changes
if (prevState.user != newState.user) {
print(' User changed: ${prevState.user} -> ${newState.user}');
}
if (prevState.counter != newState.counter) {
print(' Counter changed: ${prevState.counter} -> ${newState.counter}');
}
}
if (error != null) {
print(' Error: $error');
}
}
}
// Configure store
var store = Store<AppState>(
initialState: AppState.initialState(),
stateObservers: kDebugMode ? [DebugStateObserver()] : null,
);
```
### Detecting State Changes
Use `identical()` to check if state actually changed:
```dart
bool stateChanged = !identical(prevState, newState);
```
This is efficient because AsyncRedux uses immutable state - if the reference is the same, no change occurred.
## Custom ActionObserver for Detailed Logging
Create an `ActionObserver` for detailed dispatch tracking:
```dart
class DetailedActionObserver implements ActionObserver<AppState> {
final Map<ReduxAction, DateTime> _startTimes = {};
@override
void observe(
ReduxAction<AppState> action,
int dispatchCount, {
required bool ini,
}) {
if (ini) {
// Action started
_startTimes[action] = DateTime.now();
print('[START #$dispatchCount] ${action.runtimeType}');
} else {
// Action finished
final startTime = _startTimes.remove(action);
if (startTime != null) {
final duration = DateTime.now().difference(startTime);
print('[END #$dispatchCount] ${action.runtimeType} (${duration.inMilliseconds}ms)');
} else {
print('[END #$dispatchCount] ${action.runtimeType}');
}
}
}
}
```
## Debugging Widget Rebuilds
Use `ModelObserver` with `DefaultModelObserver` to track which widgets rebuild:
```dart
var store = Store<AppState>(
initialState: AppState.initialState(),
modelObserver: DefaultModelObserver(),
);
```
Output format:
```
Model D:1 R:1 = Rebuild:true, Connector:MyWidgetConnector, Model:MyViewModel{data}.
Model D:2 R:2 = Rebuild:false, Connector:MyWidgetConnector, Model:MyViewModel{data}.
```
- `D`: Dispatch count
- `R`: Rebuild count
- `Rebuild`: Whether widget actually rebuilt
- `Connector`: The StoreConnector type
- `Model`: ViewModel with state summary
Enable detailed output by passing `debug: this` to StoreConnector:
```dart
StoreConnector<AppState, MyViewModel>(
debug: this, // Enables connector name in output
converter: (store) => MyViewModel.fromStore(store),
builder: (context, vm) => MyWidget(vm),
)
```
## Checking Action Status in Widgets
Use context extensions to check action states:
```dart
Widget build(BuildContext context) {
// Check if action is currently running
if (context.isWaiting(LoadDataAction)) {
return CircularProgressIndicator();
}
// Check if action failed
if (context.isFailed(LoadDataAction)) {
var exception = context.exceptionFor(LoadDataAction);
return Text('Error: ${exception?.message}');
}
return Text('Data: ${context.state.data}');
}
```
## Waiting for Conditions in Tests
Use store wait methods for test debugging:
```dart
// Wait until state meets a condition
await store.waitCondition((state) => state.isLoaded);
// Wait for specific action types to complete
await store.waitAllActionTypes([LoadUserAction, LoadSettingsAction]);
// Wait for all actions to complete (empty list = wait for all)
await store.waitAllActions([]);
// Wait for action condition with access to actions in progress
await store.waitActionCondition((actionsInProgress, triggerAction) {
return actionsInProgress.isEmpty;
});
```
## Complete Debug Setup Example
```dart
void main() {
final store = Store<AppState>(
initialState: AppState.initialState(),
// Action logging (debug only)
actionObservers: kDebugMode
? [ConsoleActionObserver(), DetailedActionObserver()]
: null,
// State change logging (debug only)
stateObservers: kDebugMode
? [DebugStateObserver()]
: null,
// Widget rebuild tracking (debug only)
modelObserver: kDebugMode ? DefaultModelObserver() : null,
// Error observer (always enabled)
errorObserver: MyErrorObserver(),
);
// Debug print initial state
if (kDebugMode) {
print('Initial state: ${store.state}');
print('Dispatch count: ${store.dispatchCount}');
}
runApp(StoreProvider<AppState>(
store: store,
child: MyApp(),
));
}
```
## Debugging Tips
1. **Print state in actions**: Use `print(state)` in your reducer to see state at that moment
2. **Check initialState**: Access `action.initialState` to see state when action was dispatched (vs current `state`)
3. **Use action status**: Check `action.status.isCompletedOk` or `action.status.originalError` after dispatch
4. **Conditional logging**: Use `kDebugMode` from `package:flutter/foundation.dart` to disable in production
5. **Override toString**: Implement `toString()` on actions and state classes for better debug output
## References
URLs from the documentation:
- https://asyncredux.com/flutter/miscellaneous/logging
- https://asyncredux.com/flutter/miscellaneous/metrics
- https://asyncredux.com/flutter/miscellaneous/observing-rebuilds
- https://asyncredux.com/flutter/basics/store
- https://asyncredux.com/flutter/basics/dispatching-actions
- https://asyncredux.com/flutter/basics/wait-fail-succeed
- https://asyncredux.com/flutter/advanced-actions/action-status
- https://asyncredux.com/flutter/testing/dispatch-wait-and-expectRelated Skills
a11y-debugging
Uses Chrome DevTools MCP for accessibility (a11y) debugging and auditing based on web.dev guidelines. Use when testing semantic HTML, ARIA labels, focus states, keyboard navigation, tap targets, and color contrast.
axiom-swiftui-debugging
Use when debugging SwiftUI view updates, preview crashes, or layout issues - diagnostic decision trees to identify root causes quickly and avoid misdiagnosis under pressure
asyncredux-wait-fail-succeed
Show loading states and handle action failures in widgets. Covers `isWaiting(ActionType)` for spinners, `isFailed(ActionType)` for error states, `exceptionFor(ActionType)` for error messages, and `clearExceptionFor()` to reset failure states.
asyncredux-wait-condition
Use `waitCondition()` inside actions to pause execution until state meets criteria. Covers waiting for price thresholds, coordinating between actions, and implementing conditional workflows.
asyncredux-user-exceptions
Handle user-facing errors with UserException. Covers throwing UserException from actions, setting up UserExceptionDialog, customizing error dialogs with `onShowUserExceptionDialog`, and using UserExceptionAction for non-interrupting error display.
asyncredux-sync-actions
Creates AsyncRedux (Flutter) synchronous actions that update state immediately by implementing reduce() to return a new state.
asyncredux-streams-timers
Manage Streams and Timers with AsyncRedux. Covers creating actions to start/stop streams, storing stream subscriptions in store props, dispatching actions from stream callbacks, and proper cleanup with disposeProps().
asyncredux-state-access
Access store state in widgets using `context.state`, `context.select()`, and `context.read()`. Covers when to use each method, setting up BuildContext extensions, and optimizing widget rebuilds with selective state access.
asyncredux-setup
Initialize, setup and configure AsyncRedux in a Flutter app. Use it whenever starting a new AsyncRedux project, or when the user requests.
asyncredux-selectors
Create and cache selectors for efficient state access. Covers writing selector functions, caching with `cache1` and `cache2`, the reselect pattern, and avoiding repeated computations in widgets.
asyncredux-persistence
Implement local state persistence using Persistor. Covers creating a custom Persistor class, implementing `readState()`, `persistDifference()`, `deleteState()`, using LocalPersist helper, throttling saves, and pausing/resuming persistence with app lifecycle.
asyncredux-optimistic-update-mixin
Add the OptimisticUpdate mixin for instant UI feedback before server confirmation. Covers immediate state changes, automatic rollback on failure, and optionally notifying users of rollback.