清除缓存并对状态处置做出反应
到目前为止,我们已经了解了如何创建/更新某些状态。 但我们还没有谈论状态处置何时发生。
Riverpod 提供了多种与状态处置进行交互的方法。 这包括从延迟处置状态到对处置做出反应。
何时状态被处置,如何改变这一点?
使用代码生成时,默认情况下,当提供者程序停止被监听时,状态将被处置。
当监听器在整个帧中没有活动的监听器时,会发生这种情况。
当这种情况发生时,状态将被处置。
可以使用 keepAlive: true
选择禁用此行为。
这样做可以防止在删除所有监听器时状态被处置。
// We can specify "keepAlive" in the annotation to disable
// the automatic state destruction
(keepAlive: true)
int example(Ref ref) {
return 0;
}
启用/禁用自动处置在重新计算提供者程序时,对于是否处置状态没有影响。
重新计算提供者程序时,状态将始终被处置。
当提供者程序收到参数时,建议启用自动处置。 这是因为否则,将为每个参数组合创建一个状态,这可能会导致内存泄漏。
对状态处置做出反应
在 Riverpod 中,有几种内置的处置状态的方法:
- 提供者程序不再使用,并且处于“自动处置”模式(稍后会详细介绍)。 在这种情况下,与提供者程序的所有关联状态都将被处置。
- 提供者程序将重新计算,例如
ref.watch
。 在这种情况下,将处置以前的状态,并创建一个新状态。
在这两种情况下。发生这种情况时,您可能希望执行一些逻辑。
这可以通过 ref.onDispose
实现。
此方法允许注册监听器,当状态被处置时回调。
例如,您可能希望使用它来关闭任何活动 StreamController
:
Stream<int> example(Ref ref) {
final controller = StreamController<int>();
// When the state is destroyed, we close the StreamController.
ref.onDispose(controller.close);
// TO-DO: Push some values in the StreamController
return controller.stream;
}
不得触发副作用的 ref.onDispose
回调。
修改提供者程序内部的 onDispose
可能会导致意外行为。
还有其他有用的生命周期,例如:
ref.onCancel
当删除提供者程序的最后一个监听器时调用。ref.onResume
当onCancel
调用之后添加新的监听器时调用。
您可以根据需要多次调用 ref.onDispose
。
在提供者程序中,每个可处置的对象可随意调用一次。
这种做法使我们更容易发现我们何时忘记处置某些东西。
手动强制处置提供者程序,使用 ref.invalidate
有时,您可能希望强制处置提供者程序。
这可以通过使用 ref.invalidate
来完成,
它可以从另一个提供者程序或小部件调用。
使用 ref.invalidate
将处置当前提供者程序状态。
然后有两种可能的结果:
- 如果监听提供者程序,则将创建一个新状态。
- 如果提供者程序未被监听,则提供者程序将被完全处置。
class MyWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton(
onPressed: () {
// On click, destroy the provider.
ref.invalidate(someProvider);
},
child: const Text('dispose a provider'),
);
}
}
提供者程序可以使用 ref.invalidateSelf
使自己失效。
尽管在这种情况下,这始终会导致创建新状态。
当尝试使接收参数的提供者程序失效时, 可能会使一个特定的参数组合失效, 也可以同时使所有参数组合失效:
String label(Ref ref, String userName) {
return 'Hello $userName';
}
// ...
void onTap() {
// Invalidate all possible parameter combinations of this provider.
ref.invalidate(labelProvider);
// Invalidate a specific combination only
ref.invalidate(labelProvider('John'));
}
使用 ref.keepAlive
微调处置
如上所述,当自动处置启用时,如果在完整的一帧时间里提供者程序没有监听器,状态将被处置。
但您可能希望对此行为有更多的控制权。例如, 您可能希望保留成功网络请求的状态,但不缓存失败的请求。
这可以在启用自动处置后,使用通过 ref.keepAlive
来实现。
使用它,您可以决定何时停止自动处置状态。
Future<String> example(Ref ref) async {
final response = await http.get(Uri.parse('https://example.com'));
// We keep the provider alive only after the request has successfully completed.
// If the request failed (and threw an exception), then when the provider stops being
// listened to, the state will be destroyed.
ref.keepAlive();
// We can use the `link` to restore the auto-dispose behavior with:
// link.close();
return response.body;
}
如果重新计算提供者程序,将重新启用自动处置。
也可以使用 ref.keepAlive
的返回值使其恢复到自动处置状态。
示例:在一段特定时间内保持状态
目前,Riverpod 不提供在特定时间内保持状态的内置方法。
但是,使用我们目前看到的工具,实现这样的功能很容易且可重用。
通过使用 Timer
+ ref.keepAlive
,我们可以在特定的时间内保持状态。
为了使这个逻辑可重用,我们可以在扩展方法中实现它:
extension CacheForExtension on Ref {
/// Keeps the provider alive for [duration].
void cacheFor(Duration duration) {
// Immediately prevent the state from getting destroyed.
final link = keepAlive();
// After duration has elapsed, we re-enable automatic disposal.
final timer = Timer(duration, link.close);
// Optional: when the provider is recomputed (such as with ref.watch),
// we cancel the pending timer.
onDispose(timer.cancel);
}
}
然后,我们可以这样使用它:
Future<Object> example(Ref ref) async {
/// Keeps the state alive for 5 minutes
ref.cacheFor(const Duration(minutes: 5));
return http.get(Uri.https('example.com'));
}
可以调整此逻辑以满足您的需求。
例如,仅当提供者程序在特定时间内未被监听时,
才可以使用 ref.onCancel
/ref.onResume
处置状态。