名称: asyncredux-action-status 描述: 使用ActionStatus在dispatch返回后立即检查AsyncRedux(Flutter)action的完成状态。仅当需要知道action是否完成、是否因错误失败、产生什么错误,或如何基于成功或失败进行导航时使用。
AsyncRedux中的ActionStatus
ActionStatus对象提供有关action是否成功完成或遇到错误的信息。它由dispatchAndWait()和相关方法返回。
获取ActionStatus
使用dispatchAndWait()在action完成后获取状态:
var status = await dispatchAndWait(MyAction());
在action内部,也可以使用:
var status = await dispatchAndWait(SomeOtherAction());
ActionStatus属性
完成状态
isCompleted: 如果action已执行完成(无论成功或失败),返回trueisCompletedOk: 如果action在before()和reduce()方法中没有错误完成,返回trueisCompletedFailed: 如果action遇到错误(与isCompletedOk相反),返回true
错误信息
originalError:before()或reduce()方法最初抛出的错误,在任何修改之前wrappedError: 经过action的wrapError()方法处理后的错误
执行跟踪
这些属性跟踪哪些生命周期方法已完成:
hasFinishedMethodBefore: 如果before()方法完成,返回truehasFinishedMethodReduce: 如果reduce()方法完成,返回truehasFinishedMethodAfter: 如果after()方法完成,返回true
注意:执行跟踪属性主要用于测试和调试。在生产代码中,专注于isCompletedOk和isCompletedFailed。
常见使用案例
成功后的条件导航
最常见的生产用途是在导航前检查action是否成功:
// 在widget回调中
Future<void> _onSavePressed() async {
var status = await context.dispatchAndWait(SaveFormAction());
if (status.isCompletedOk) {
Navigator.pop(context);
}
}
另一个带push导航的示例:
Future<void> _onLoginPressed() async {
var status = await context.dispatchAndWait(LoginAction(
email: emailController.text,
password: passwordController.text,
));
if (status.isCompletedOk) {
Navigator.pushReplacementNamed(context, '/home');
}
// 如果失败,错误将通过UserExceptionDialog显示
}
测试Action错误
使用ActionStatus验证action是否抛出预期错误:
test('MyAction fails with invalid input', () async {
var store = Store<AppState>(initialState: AppState.initial());
var status = await store.dispatchAndWait(MyAction(value: -1));
expect(status.isCompletedFailed, isTrue);
expect(status.wrappedError, isA<UserException>());
expect((status.wrappedError as UserException).msg, "Value must be positive");
});
测试Action成功
test('SaveAction completes successfully', () async {
var store = Store<AppState>(initialState: AppState.initial());
var status = await store.dispatchAndWait(SaveAction(data: validData));
expect(status.isCompletedOk, isTrue);
expect(store.state.saved, isTrue);
});
检查原始错误与包装后错误
当您的action使用wrapError()转换错误时,可以检查两者:
class MyAction extends AppAction {
@override
Future<AppState?> reduce() async {
throw Exception('Network error');
}
@override
Object? wrapError(Object error, StackTrace stackTrace) {
return UserException('Could not save. Please try again.');
}
}
// 在测试中:
var status = await store.dispatchAndWait(MyAction());
expect(status.originalError, isA<Exception>()); // 原始Exception
expect(status.wrappedError, isA<UserException>()); // 包装后的UserException
Action生命周期与状态
action生命周期按此顺序运行:
before()- 首先运行,可用于前置条件reduce()- 其次运行(仅当before()成功)after()- 最后运行,始终执行(像finally块)
isCompletedOk属性仅当before()和reduce()都没有错误完成时为true。注意,after()中的错误不影响isCompletedOk。
如果before()抛出错误,reduce()不会运行,但after()仍会执行。
最佳实践
-
使用状态更改进行UI更新: 在生产中,更倾向于检查状态更改而不是action状态。保留ActionStatus用于需要基于成功/失败执行副作用(如导航)的情况。
-
使用
isCompletedOk进行导航: 常见模式是在action成功后导航:if (status.isCompletedOk) Navigator.pop(context); -
在测试中使用
wrappedError: 当测试错误处理时,检查wrappedError以查看用户实际将看到的内容(经过wrapError()处理后)。 -
使用
originalError进行调试: 当需要查看任何转换前的底层错误时,使用originalError。
参考
文档中的URL:
- https://asyncredux.com/flutter/advanced-actions/action-status
- https://asyncredux.com/flutter/basics/dispatching-actions
- https://asyncredux.com/flutter/basics/failed-actions
- https://asyncredux.com/flutter/advanced-actions/errors-thrown-by-actions
- https://asyncredux.com/flutter/advanced-actions/before-and-after-the-reducer
- https://asyncredux.com/flutter/advanced-actions/redux-action
- https://asyncredux.com/flutter/miscellaneous/navigation
- https://asyncredux.com/flutter/testing/store-tester
- https://asyncredux.com/flutter/testing/dispatch-wait-and-expect
- https://asyncredux.com/flutter/testing/testing-user-exceptions