with one click
asyncredux-base-action
// 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.
// 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.
| name | asyncredux-base-action |
| description | 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. |
Every AsyncRedux application should define an abstract base action class that all actions extend. This provides a central place for:
Create an abstract class extending ReduxAction<AppState>.
The recomended name is AppAction, in file app_action.dart:
abstract class AppAction extends ReduxAction<AppState> {
// All your actions will extend this class
}
Then extend AppAction instead of ReduxAction<AppState> in all your actions:
class IncrementCounter extends AppAction {
@override
AppState reduce() => state.copy(counter: state.counter + 1);
}
When your state has nested objects, add getters to simplify access:
abstract class AppAction extends ReduxAction<AppState> {
// Shortcuts to nested state parts
User get user => state.user;
Settings get settings => state.settings;
IList<Todo> get todos => state.todos;
Cart get cart => state.cart;
}
Now actions can write cleaner code:
class UpdateUserName extends AppAction {
final String name;
UpdateUserName(this.name);
@override
AppState reduce() {
// Instead of: state.user.name
// You can write: user.name
return state.copy(user: user.copy(name: name));
}
}
For common data lookups, add selector methods directly to your base action:
abstract class AppAction extends ReduxAction<AppState> {
// Getters for state parts
User get user => state.user;
IList<Item> get items => state.items;
// Selector methods
Item? findItemById(String id) =>
items.firstWhereOrNull((item) => item.id == id);
List<Item> get completedItems =>
items.where((item) => item.isCompleted).toList();
bool get isLoggedIn => user.isAuthenticated;
}
Actions can then use these selectors:
class MarkItemComplete extends AppAction {
final String itemId;
MarkItemComplete(this.itemId);
@override
AppState reduce() {
final item = findItemById(itemId);
if (item == null) return null; // No change
return state.copy(
items: items.replaceFirstWhere(
(i) => i.id == itemId,
item.copy(isCompleted: true),
),
);
}
}
For most applications, it's better to use instead a dedicated selector class to keep the base action clean:
class ActionSelect {
final AppState state;
ActionSelect(this.state);
Item? findById(String id) =>
state.items.firstWhereOrNull((item) => item.id == id);
List<Item> get completed =>
state.items.where((item) => item.isCompleted).toList();
List<Item> get pending =>
state.items.where((item) => !item.isCompleted).toList();
}
abstract class AppAction extends ReduxAction<AppState> {
ActionSelect get select => ActionSelect(state);
}
These namespaces selectors under select, enabling IDE autocompletion:
class ProcessItem extends AppAction {
final String itemId;
ProcessItem(this.itemId);
@override
AppState reduce() {
// IDE autocomplete shows: select.findById, select.completed, etc.
final item = select.findById(itemId);
// ...
}
}
For dependency injection, override the env getter in your base action:
class Environment {
final ApiClient api;
final AuthService auth;
final AnalyticsService analytics;
Environment({
required this.api,
required this.auth,
required this.analytics,
});
}
abstract class AppAction extends ReduxAction<AppState> {
// Type-safe access to environment
@override
Environment get env => super.env as Environment;
// Convenience getters for common services
ApiClient get api => env.api;
AuthService get auth => env.auth;
}
Actions can then use services directly:
class FetchUserProfile extends AppAction {
@override
Future<AppState?> reduce() async {
// Uses the api getter from base action
final profile = await api.getUserProfile();
return state.copy(user: profile);
}
}
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.
Initialize, setup and configure AsyncRedux in a Flutter app. Use it whenever starting a new AsyncRedux project, or when the user requests.
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.
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.