Skip to main content

Action Subclassing

Suppose we have the following app state:

class AppState {  
List<Item> items;
Item? selectedItem;
// ...
}

class Item {
String id;
// ...
}

And then we have an action, which selects an item by id:

class SelectItem extends AppAction {
final int id;
SelectItem(this.id);

AppState reduce() {
Item? item = state.items.firstWhereOrNull((item) => item.id == id);
if (item == null) throw UserException('Item not found');
return state.copy(selectedItem: item);
}
}

You would use it like this:

var item = Item('A'); 
dispatch(SelectItem(item));

Since all actions extend ReduxAction<AppState>, you may use object-oriented principles to reduce boilerplate. Start by creating an abstract base action class to allow easier access to the sub-states of your store.

You could name is BaseAction, Action or any other name. Here, we'll call it AppAction:

abstract class AppAction extends ReduxAction<AppState> {

// Getter shortcuts
List<Item> get items => state.items;
Item get selectedItem => state.selectedItem;

// Selectors
Item? findById(int id) => items.firstWhereOrNull((item) => item.id == id);
Item? searchByText(String text) => items.firstWhereOrNull((item) => item.text.contains(text));
int get selectedIndex => items.indexOf(selectedItem);
}

And then your actions have an easier time accessing the store state:

class SelectItem extends AppAction {
final int id;
SelectItem(this.id);

AppState reduce() {
Item? item = findById(id); // Here!
if (item == null) throw UserException('Item not found');
return state.copy(selected: item);
}
}

The difference above is that, instead of writing:

Item? item = state.items.firstWhereOrNull((item) => item.id == id); 

You can simply write:

Item? item = findById(id);

It may seem a small reduction of boilerplate, but it adds up.

In practice, your base action class may end up containing a lot of elaborate "selector methods", which then can be used by all your actions.

The only requirement is that your actions now extend AppAction instead of ReduxAction<AppState>.


This concludes the explanation of advanced action features. You now know how to use before() and after() methods, wrap the reducer, abort the action dispatch, handle action errors, check actions status, and subclass actions.

The next section will explain how to uncouple accessing the store state, by using the connector pattern.