返回 Skill 列表
extension
分类: 开发与工程无需 API Key

asyncredux-base-action

为您的应用程序创建一个自定义的基础动作类。涵盖向状态部分添加getter快捷方式、添加选择器方法、实现共享的wrapError逻辑以及建立项目范围的动作约定。

person作者: jakexiaohubgithub

Creating a Custom Base Action Class

Every AsyncRedux application should define an abstract base action class that all actions extend. This provides a central place for:

  • Convenient getter shortcuts to state parts
  • Selector methods for common queries
  • Shared error handling logic
  • Type-safe environment access
  • Project-wide conventions

Basic Base Action Setup

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);
}

Adding Getter Shortcuts to State Parts

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));
  }
}

Adding Selector Methods

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),
      ),
    );
  }
}

Using a Separate Selector Class

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);
    // ...
  }
}

Type-Safe Environment Access

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);
  }
}

References

URLs from the documentation:

  • https://asyncredux.com/flutter/advanced-actions/redux-action
  • https://asyncredux.com/flutter/advanced-actions/action-selectors
  • https://asyncredux.com/flutter/advanced-actions/errors-thrown-by-actions
  • https://asyncredux.com/flutter/advanced-actions/wrapping-the-reducer
  • https://asyncredux.com/flutter/advanced-actions/action-mixins
  • https://asyncredux.com/flutter/advanced-actions/aborting-the-dispatch
  • https://asyncredux.com/flutter/basics/actions-and-reducers
  • https://asyncredux.com/flutter/basics/state
  • https://asyncredux.com/flutter/basics/using-the-store-state
  • https://asyncredux.com/flutter/miscellaneous/business-logic
  • https://asyncredux.com/flutter/miscellaneous/dependency-injection