Before and after the reducer
The ReduxAction class comes with two optional methods, before() and after(),
Overview
Suppose you want to stop the user from touching the screen while an action MyAction is running.
You can do this by adding a modal barrier before the action starts and removing it after it ends.
It is common to have side effects before and after the reducer runs.
To help with that, you can override the methods before() and after(),
which run before and after the reducer (method reduce()).
Note: The
reduce()method is required, butbefore()andafter()are optional. By default, they do nothing.
Before
The before() method runs before the reducer.
To run it synchronously, return void.
To run it asynchronously, return Future<void>:
// Sync
void before() { ... }
// Async
Future<void> before() async { ... }
If before() throws an error, then reduce() will not run.
This lets you use before() to check for preconditions
and throw an error when needed to prevent the reducer from running. For example:
// Shows a dialog if there is no internet connection,
// and prevents the reducer from running.
Future<void> before() async {
if (!await hasInternetConnection())
throw UserException('No internet connection');
}
Note: If
before()returns a future, then the action is async (it completes in a later microtask), even ifreduce()is sync.
After
The after() method runs after the reducer.
It works like a finally block, because it always runs,
even if before() or reduce() throws an error.
This makes it safe to undo anything done in before(), even when something goes wrong later.
Note: Make sure
after()itself does not throw an error. If it does, the error will be thrown asynchronously, so it does not interfere with the action, but it will still appear in the console.
Example
In the modal barrier example, we can dispatch an action that turns the barrier on and off.
First define BarrierAction:
class BarrierAction extends AppAction {
final bool hasBarrier;
BarrierAction(this.hasBarrier);
AppState reduce() => state.copy(hasBarrier: hasBarrier);
}
Then your widget tree shows the modal barrier only when hasBarrier is true:
return context.state.hasBarrier
? ModalBarrier()
: Container();
Now, use before() and after() to dispatch BarrierAction:
class MyAction extends AppAction {
Future<AppState> reduce() async {
String description = await read(Uri.http("numbersapi.com","${state.counter}");
return state.copy(description: description);
}
void before() => dispatch(BarrierAction(true));
void after() => dispatch(BarrierAction(false));
}
You can see BarrierAction used in
this example
Creating a Mixin
To reuse this behavior in many actions, create a mixin:
mixin Barrier on AppAction {
void before() => dispatch(BarrierAction(true));
void after() => dispatch(BarrierAction(false));
}
Then write with Barrier:
class MyAction extends AppAction with Barrier {
Future<AppState> reduce() async {
String description = await read(Uri.http("numbersapi.com","${state.counter}");
return state.copy(description: description);
}
}
Try running the: Before and After Example.