name: 异步Redux状态访问
description: 使用 context.state、context.select() 和 context.read() 在widget中访问存储状态。涵盖何时使用每种方法、设置BuildContext扩展以及通过选择性状态访问优化widget重建。
BuildContext扩展设置
要在widget中访问应用程序状态,首先定义一个BuildContext扩展。将此添加到您的项目中(通常在所有widget可以导入的共享文件中):
extension BuildContextExtension on BuildContext {
AppState get state => getState<AppState>();
AppState read() => getRead<AppState>();
R select<R>(R Function(AppState state) selector) => getSelect<AppState, R>(selector);
R? event<R>(Evt<R> Function(AppState state) selector) => getEvent<AppState, R>(selector);
}
将AppState替换为您实际的状态类名。
三种状态访问方法
context.state
提供对整个状态对象的访问。所有使用context.state的widget将在存储状态更改时自动重建(任何部分)。
Widget build(BuildContext context) {
return Text('Counter: ${context.state.counter}');
}
context.select()
仅检索特定状态部分。这更高效,因为它只在所选状态部分更改时重建widget。
Widget build(BuildContext context) {
var counter = context.select((state) => state.counter);
return Text('Counter: $counter');
}
context.read()
检索状态而不触发重建。在事件处理器、initState或任何您需要一次性读取状态而不订阅更改的地方使用。
void _onButtonPressed() {
var currentCount = context.read().counter;
print('Current count is $currentCount');
}
何时使用每种方法
| 方法 | 使用在 | 触发重建? | 最佳用于 |
|---|---|---|---|
context.state |
build方法 |
是,在任何状态更改时 | 简单widget或当您需要多个状态属性时 |
context.select() |
build方法 |
仅在所选部分更改时 | 性能敏感widget |
context.read() |
initState、事件处理器、回调 |
否 | 一次性读取、按钮处理器 |
访问多个状态属性
当您需要多个状态片段时,有两个选项:
选项1:多个select调用
Widget build(BuildContext context) {
var name = context.select((state) => state.user.name);
var email = context.select((state) => state.user.email);
var itemCount = context.select((state) => state.items.length);
// Widget仅在name、email或itemCount更改时重建
return Text('$name ($email) - $itemCount items');
}
选项2:使用Dart记录进行组合选择
Widget build(BuildContext context) {
var (name, email) = context.select((state) => (state.user.name, state.user.email));
return Text('$name ($email)');
}
用于操作状态的附加上下文方法
除了状态访问外,上下文扩展还提供了跟踪异步操作进度的方法:
Widget build(BuildContext context) {
// 检查操作是否正在运行
if (context.isWaiting(LoadDataAction)) {
return CircularProgressIndicator();
}
// 检查操作是否失败
if (context.isFailed(LoadDataAction)) {
var exception = context.exceptionFor(LoadDataAction);
return Text('Error: ${exception?.message}');
}
// 显示数据
return Text('Data: ${context.state.data}');
}
可用方法:
context.isWaiting(ActionType)- 如果操作正在进行中,则返回truecontext.isFailed(ActionType)- 如果操作最近失败,则返回truecontext.exceptionFor(ActionType)- 获取失败操作的异常context.clearExceptionFor(ActionType)- 手动清除存储的异常
Widget选择器模式
对于复杂选择逻辑,创建一个WidgetSelect类来组织可重用的选择器:
class WidgetSelect {
final BuildContext context;
WidgetSelect(this.context);
// Getter快捷方式
List<Item> get items => context.select((state) => state.items);
User get currentUser => context.select((state) => state.user);
// 自定义查找方法
Item? findById(int id) => context.select(
(state) => state.items.firstWhereOrNull((item) => item.id == id)
);
List<Item> searchByText(String text) => context.select(
(state) => state.items.where((item) => item.name.contains(text)).toList()
);
}
将其添加到您的BuildContext扩展中:
extension BuildContextExtension on BuildContext {
AppState get state => getState<AppState>();
// ... 其他方法 ...
WidgetSelect get selector => WidgetSelect(this);
}
在widget中使用:
Widget build(BuildContext context) {
var user = context.selector.currentUser;
var item = context.selector.findById(42);
return Text('${user.name}: ${item?.name}');
}
重要指南
避免在selector中使用context.state
永远不要在selector函数中使用context.state。这会破坏选择性重建的目的:
// 错误 - 在任何状态更改时重建
var items = context.select((state) => context.state.items.where(...));
// 正确 - 仅在items更改时重建
var items = context.select((state) => state.items.where(...));
永远不要嵌套context.select调用
嵌套context.select会导致错误。始终在顶层应用选择:
// 错误 - 会导致错误
var result = context.select((state) =>
context.select((s) => s.items).where(...) // 嵌套select!
);
// 正确
var items = context.select((state) => state.items);
var result = items.where(...);
调试重建
要观察widget何时重建(用于性能调试),使用ModelObserver:
var store = Store<AppState>(
initialState: AppState.initialState(),
modelObserver: DefaultModelObserver(),
);
DefaultModelObserver记录控制台输出,显示:
- 重建是否发生
- 哪个连接器/widget触发了它
- 视图模型状态
示例输出:
Model D:1 R:1 = Rebuild:true, Connector:MyWidgetConnector, Model:MyViewModel{counter: 5}
参考
文档URLs:
- https://asyncredux.com/sitemap.xml
- https://asyncredux.com/flutter/basics/using-the-store-state
- https://asyncredux.com/flutter/miscellaneous/widget-selectors
- https://asyncredux.com/flutter/miscellaneous/observing-rebuilds
- https://asyncredux.com/flutter/miscellaneous/cached-selectors
- https://asyncredux.com/flutter/basics/store
- https://asyncredux.com/flutter/advanced-actions/action-selectors
- https://asyncredux.com/flutter/connector/store-connector
- https://asyncredux.com/flutter/intro
- https://asyncredux.com/flutter/basics/wait-fail-succeed