名称: asyncredux-before-after
描述: 实现动作生命周期方法 before() 和 after()。涵盖运行预处理检查、显示/隐藏模态屏障、after() 中的清理逻辑,以及理解 after() 总是运行(类似于finally块)。
AsyncRedux 前后方法
动作生命周期概述
每个 ReduxAction 有三个按顺序执行的生命周期方法:
before()- 首先运行,在reducer之前reduce()- 主要的reducer(必需)after()- 最后运行,总是执行
只有 reduce() 是必需的。before() 和 after() 方法是用于管理副作用的可选钩子。
before() 方法
before() 方法在reducer运行之前执行。它可以是同步的或异步的。
同步 before()
class MyAction extends ReduxAction<AppState> {
@override
void before() {
// 在reduce()之前同步运行
print('动作开始');
}
@override
AppState? reduce() {
return state.copy(counter: state.counter + 1);
}
}
异步 before()
class MyAction extends ReduxAction<AppState> {
@override
Future<void> before() async {
// 在reduce()之前异步运行
await validatePermissions();
}
@override
Future<AppState?> reduce() async {
final data = await fetchData();
return state.copy(data: data);
}
}
before() 中的预处理检查
如果 before() 抛出错误,reduce() 将不会运行。这使其非常适合验证:
class FetchUserData extends ReduxAction<AppState> {
@override
Future<void> before() async {
if (!await hasInternetConnection()) {
throw UserException('无网络连接');
}
}
@override
Future<AppState?> reduce() async {
// 仅当before()完成无误时运行
final user = await api.fetchUser();
return state.copy(user: user);
}
}
常见 before() 使用场景
- 验证预处理条件(身份验证、权限)
- 检查网络连接
- 显示加载指示器或模态屏障
- 记录动作开始以进行分析
- 分发先决条件动作
after() 方法
after() 方法在reducer完成后执行。其关键特性是:它总是运行,即使 before() 或 reduce() 抛出错误。这使其类似于 finally 块。
基本 after()
class MyAction extends ReduxAction<AppState> {
@override
AppState? reduce() {
return state.copy(counter: state.counter + 1);
}
@override
void after() {
// 无论成功或失败,总是运行
print('动作完成');
}
}
确保清理
由于 after() 总是运行,它非常适合清理操作:
class SaveDocument extends ReduxAction<AppState> {
@override
Future<void> before() async {
dispatch(ShowSavingIndicatorAction(true));
}
@override
Future<AppState?> reduce() async {
await api.saveDocument(state.document);
return state.copy(lastSaved: DateTime.now());
}
@override
void after() {
// 即使保存失败也隐藏指示器
dispatch(ShowSavingIndicatorAction(false));
}
}
重要:永远不要从 after() 抛出错误
after() 方法不应抛出错误。从 after() 抛出的任何异常将异步出现在控制台中,且无法正常捕获:
// 错误 - 不要在after()中抛出
@override
void after() {
if (someCondition) {
throw Exception('这会导致问题');
}
}
// 正确 - 优雅处理错误
@override
void after() {
try {
cleanup();
} catch (e) {
// 记录但不抛出
logger.error('清理失败: $e');
}
}
常见 after() 使用场景
- 隐藏加载指示器或模态屏障
- 关闭数据库连接或文件句柄
- 释放临时资源
- 记录动作完成以进行分析
- 分发后续动作
模态屏障模式
一个常见模式是在异步操作期间显示模态屏障(阻塞覆盖层):
class MyAction extends ReduxAction<AppState> {
@override
Future<AppState?> reduce() async {
String description = await read(Uri.http("numbersapi.com", "${state.counter}"));
return state.copy(description: description);
}
@override
void before() => dispatch(BarrierAction(true));
@override
void after() => dispatch(BarrierAction(false));
}
BarrierAction 将更新状态以显示/隐藏加载覆盖层:
class BarrierAction extends ReduxAction<AppState> {
final bool show;
BarrierAction(this.show);
@override
AppState reduce() => state.copy(showBarrier: show);
}
创建可重用混合
对于重复使用的模式,创建一个混合:
mixin Barrier on ReduxAction<AppState> {
@override
void before() {
super.before();
dispatch(BarrierAction(true));
}
@override
void after() {
dispatch(BarrierAction(false));
super.after();
}
}
然后应用于任何动作:
class FetchData extends ReduxAction<AppState> with Barrier {
@override
Future<AppState?> reduce() async {
// 在此运行前自动显示屏障
final data = await api.fetchData();
return state.copy(data: data);
// 即使出错,后自动隐藏屏障
}
}
多个混合
您可以组合多个混合:
class ImportantAction extends ReduxAction<AppState> with Barrier, NonReentrant {
@override
Future<AppState?> reduce() async {
// 既有模态屏障又防止重复分发
return state;
}
}
错误处理流程
理解错误如何与生命周期交互:
class MyAction extends ReduxAction<AppState> {
@override
Future<void> before() async {
// 如果此抛出,reduce()被跳过,after()仍运行
}
@override
Future<AppState?> reduce() async {
// 如果此抛出,状态不变,after()仍运行
}
@override
void after() {
// 无论上述错误如何,总是运行
}
}
检查完成情况
使用 ActionStatus 确定哪些方法完成:
var status = await dispatchAndWait(MyAction());
if (status.hasFinishedMethodBefore) {
print('before() 完成');
}
if (status.hasFinishedMethodReduce) {
print('reduce() 完成');
}
if (status.hasFinishedMethodAfter) {
print('after() 完成');
}
if (status.isCompletedOk) {
print('before() 和 reduce() 均完成无误');
}
if (status.isCompletedFailed) {
print('错误: ${status.originalError}');
}
与 abortDispatch() 的关系
如果 abortDispatch() 返回 true,则没有生命周期方法运行:
class MyAction extends ReduxAction<AppState> {
@override
bool abortDispatch() => state.user == null;
@override
void before() {
// 如果abortDispatch()返回true,则跳过
}
@override
AppState? reduce() {
// 如果abortDispatch()返回true,则跳过
}
@override
void after() {
// 如果abortDispatch()返回true,则跳过
}
}
完整示例
class SubmitForm extends ReduxAction<AppState> {
final String formData;
SubmitForm(this.formData);
@override
Future<void> before() async {
// 验证预处理条件
if (state.user == null) {
throw UserException('请先登录');
}
if (!await checkInternetConnection()) {
throw UserException('无网络连接');
}
// 显示加载状态
dispatch(SetSubmittingAction(true));
}
@override
Future<AppState?> reduce() async {
final result = await api.submitForm(formData);
return state.copy(
lastSubmission: result,
submissionCount: state.submissionCount + 1,
);
}
@override
void after() {
// 即使出错,总是隐藏加载状态
dispatch(SetSubmittingAction(false));
// 记录完成
analytics.log('form_submitted');
}
}
内置混合使用 before() 和 after()
几个AsyncRedux混合在内部使用这些方法:
| 混合 | 使用 before() | 使用 after() | 目的 |
|---|---|---|---|
CheckInternet |
是 | 否 | 验证连接性,离线时显示对话框 |
AbortWhenNoInternet |
是 | 否 | 离线时静默中止 |
Throttle |
否 | 是 | 限制执行频率 |
NonReentrant |
是 | 是 | 防止重复分发 |
Retry |
否 | 是 | 失败时重试 |
Debounce |
否 | 否 | 等待输入暂停(使用 wrapReduce) |
使用这些混合时,请注意它们可能已覆盖 before() 或 after()。如果需要组合行为,请调用 super.before() 和 super.after()。
参考
文档URL:
- https://asyncredux.com/flutter/advanced-actions/before-and-after-the-reducer
- https://asyncredux.com/flutter/advanced-actions/redux-action
- https://asyncredux.com/flutter/advanced-actions/errors-thrown-by-actions
- https://asyncredux.com/flutter/advanced-actions/action-status
- https://asyncredux.com/flutter/advanced-actions/action-mixins
- https://asyncredux.com/flutter/advanced-actions/aborting-the-dispatch
- https://asyncredux.com/flutter/advanced-actions/wrapping-the-reducer