ワンクリックで
asyncredux-abort-dispatch
// Stops an AsyncRedux (Flutter) action from dispatching. Use only when the user mentions abortDispatch(), or explicitly asks to abort or prevent dispatch under certain conditions.
// Stops an AsyncRedux (Flutter) action from dispatching. Use only when the user mentions abortDispatch(), or explicitly asks to abort or prevent dispatch under certain conditions.
| name | asyncredux-abort-dispatch |
| description | Stops an AsyncRedux (Flutter) action from dispatching. Use only when the user mentions abortDispatch(), or explicitly asks to abort or prevent dispatch under certain conditions. |
The abortDispatch() method is an optional method on ReduxAction that lets you
conditionally prevent an action from executing. When this method returns true, the
entire action is skipped—before(), reduce(), and after() will NOT run, and state
remains unchanged.
class MyAction extends ReduxAction<AppState> {
@override
bool abortDispatch() {
// Return true to abort, false to proceed
return someCondition;
}
@override
AppState? reduce() {
// Only runs if abortDispatch() returned false
return state.copy(/* ... */);
}
}
The simplest use case is checking a condition before allowing the action to proceed:
class LoadUserProfile extends ReduxAction<AppState> {
@override
bool abortDispatch() => state.user == null;
@override
Future<AppState?> reduce() async {
// Only runs if user is logged in
final profile = await api.fetchProfile(state.user!.id);
return state.copy(profile: profile);
}
}
When abortDispatch() returns true, the complete action lifecycle is skipped:
class MyAction extends ReduxAction<AppState> {
@override
bool abortDispatch() => state.shouldSkip; // If true:
@override
void before() {
// NOT called when aborted
}
@override
AppState? reduce() {
// NOT called when aborted
}
@override
void after() {
// NOT called when aborted
}
}
This differs from throwing an error in before(), which would still cause after() to
run.
A common pattern is creating a base action that requires authentication:
/// Base action that requires an authenticated user
abstract class AuthenticatedAction extends ReduxAction<AppState> {
@override
bool abortDispatch() => state.user == null;
}
/// Actions extending this will only run when user is logged in
class FetchUserOrders extends AuthenticatedAction {
@override
Future<AppState?> reduce() async {
// Safe to use state.user! here - abortDispatch ensures it's not null
final orders = await api.getOrders(state.user!.id);
return state.copy(orders: orders);
}
}
class UpdateUserSettings extends AuthenticatedAction {
final Settings newSettings;
UpdateUserSettings(this.newSettings);
@override
Future<AppState?> reduce() async {
await api.updateSettings(state.user!.id, newSettings);
return state.copy(settings: newSettings);
}
}
You can combine multiple abort conditions in a base action:
abstract class AppAction extends ReduxAction<AppState> {
// Override in subclasses to add action-specific abort logic
bool shouldAbort() => false;
@override
bool abortDispatch() {
// Global abort conditions
if (state.isMaintenanceMode) return true;
if (state.isAppLocked) return true;
// Action-specific abort conditions
return shouldAbort();
}
}
class RefreshData extends AppAction {
@override
bool shouldAbort() {
// Don't refresh if data is still fresh
return state.lastRefresh != null &&
DateTime.now().difference(state.lastRefresh!) < Duration(minutes: 5);
}
@override
Future<AppState?> reduce() async {
final data = await api.fetchData();
return state.copy(data: data, lastRefresh: DateTime.now());
}
}
Use abortDispatch() to implement role-based access control:
abstract class AdminAction extends ReduxAction<AppState> {
@override
bool abortDispatch() => state.user?.role != UserRole.admin;
}
class DeleteAllUsers extends AdminAction {
@override
Future<AppState?> reduce() async {
// Only admins can reach this code
await api.deleteAllUsers();
return state.copy(users: []);
}
}
Prevent actions when features are disabled:
class UsePremiumFeature extends ReduxAction<AppState> {
@override
bool abortDispatch() => !state.user!.isPremium;
@override
AppState? reduce() {
// Premium-only functionality
return state.copy(/* ... */);
}
}
AsyncRedux provides AbortWhenNoInternet, a mixin that silently aborts actions when
there's no internet connection:
class FetchLatestNews extends AppAction with AbortWhenNoInternet {
@override
Future<AppState?> reduce() async {
// Only runs if internet is available
final news = await api.fetchNews();
return state.copy(news: news);
}
}
Key characteristics of AbortWhenNoInternet:
Compare with CheckInternet which shows an error dialog instead of silently aborting.
Choose the right approach for your use case:
| Approach | after() runs? | Shows error? | Use when |
|---|---|---|---|
abortDispatch() returns true | No | No | Silently skip action |
Throw in before() | Yes | Yes (if UserException) | Show error to user |
// Silent abort - user doesn't know action was skipped
class SilentRefresh extends ReduxAction<AppState> {
@override
bool abortDispatch() => state.isOffline;
// ...
}
// Visible error - user sees message
class ExplicitRefresh extends ReduxAction<AppState> {
@override
void before() {
if (state.isOffline) {
throw UserException('Cannot refresh while offline');
}
}
// ...
}
Good use cases:
Consider alternatives when:
UserException in before())before() + after() pattern)Throttle or Debounce mixins)NonReentrant mixin)// Base action with common abort logic
abstract class AppAction extends ReduxAction<AppState> {
@override
bool abortDispatch() {
// Global maintenance mode check
if (state.maintenanceMode) return true;
return false;
}
}
// Authenticated action that also checks maintenance mode
abstract class AuthenticatedAction extends AppAction {
@override
bool abortDispatch() {
// Check parent conditions first
if (super.abortDispatch()) return true;
// Then check authentication
return state.currentUser == null;
}
}
// Admin action with full authorization chain
abstract class AdminAction extends AuthenticatedAction {
@override
bool abortDispatch() {
if (super.abortDispatch()) return true;
return state.currentUser?.role != UserRole.admin;
}
}
// Concrete action using the hierarchy
class BanUser extends AdminAction {
final String userId;
BanUser(this.userId);
@override
Future<AppState?> reduce() async {
// Only reaches here if:
// 1. Not in maintenance mode
// 2. User is logged in
// 3. User is an admin
await api.banUser(userId);
return state.copy(
users: state.users.where((u) => u.id != userId).toList(),
);
}
}
abortDispatch() is checked before before(), reduce(), and after()URLs from the documentation:
Inject dependencies into actions using the environment, dependencies, and configuration pattern. Covers creating an Environment enum, a Dependencies class, passing them to the Store, accessing them from actions and widgets, and using dependency injection for testability.
Create a custom base action class for your app. Covers adding getter shortcuts to state parts, adding selector methods, implementing shared wrapError logic, and establishing project-wide action conventions.
Initialize, setup and configure AsyncRedux in a Flutter app. Use it whenever starting a new AsyncRedux project, or when the user requests.
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.
Creates AsyncRedux (Flutter) actions that return null from reduce() to not change the state. Such actions can still do side effects, dispatch other actions, or do nothing.
Creates AsyncRedux (Flutter) asynchronous actions for API calls, database operations, and other async work.