跳到主要内容

清除缓存并对状态处置做出反应

到目前为止,我们已经了解了如何创建/更新某些状态。 但我们还没有谈论状态处置何时发生。

Riverpod 提供了多种与状态处置进行交互的方法。 这包括从延迟处置状态到对处置做出反应。

何时状态被处置,如何改变这一点?

使用代码生成时,默认情况下,当提供者程序停止被监听时,状态将被处置。
当监听器在整个帧中没有活动的监听器时,会发生这种情况。 当这种情况发生时,状态将被处置。

可以使用 keepAlive: true 选择禁用此行为。
这样做可以防止在删除所有监听器时状态被处置。

// 我们可以在注解中指定 "keepAlive" 来禁用状态自动处置功能
(keepAlive: true)
int example(ExampleRef ref) {
return 0;
}
备注

启用/禁用自动处置在重新计算提供者程序时,对于是否处置状态没有影响。
重新计算提供者程序时,状态将始终被处置。

警告

当提供者程序收到参数时,建议启用自动处置。 这是因为否则,将为每个参数组合创建一个状态,这可能会导致内存泄漏。

对状态处置做出反应

在 Riverpod 中,有几种内置的处置状态的方法:

  • 提供者程序不再使用,并且处于“自动处置”模式(稍后会详细介绍)。 在这种情况下,与提供者程序的所有关联状态都将被处置。
  • 提供者程序将重新计算,例如 ref.watch。 在这种情况下,将处置以前的状态,并创建一个新状态。

在这两种情况下。发生这种情况时,您可能希望执行一些逻辑。
这可以通过 ref.onDispose 实现。 此方法允许注册监听器,当状态被处置时回调。

例如,您可能希望使用它来关闭任何活动 StreamController


Stream<int> example(ExampleRef ref) {
final controller = StreamController<int>();

// 当状态被处置时,我们关闭 StreamController。
ref.onDispose(controller.close);

// TO-DO: 在 StreamController 中推送一些值
return controller.stream;
}
警告

不得触发副作用的 ref.onDispose 回调。 修改提供者程序内部的 onDispose 可能会导致意外行为。

信息

还有其他有用的生命周期,例如:

  • ref.onCancel 当删除提供者程序的最后一个监听器时调用。
  • ref.onResumeonCancel 调用之后添加新的监听器时调用。
信息

您可以根据需要多次调用 ref.onDispose。 在提供者程序中,每个可处置的对象可随意调用一次。 这种做法使我们更容易发现我们何时忘记处置某些东西。

手动强制处置提供者程序,使用 ref.invalidate

有时,您可能希望强制处置提供者程序。 这可以通过使用 ref.invalidate 来完成, 它可以从另一个提供者程序或小部件调用。

使用 ref.invalidate 将处置当前提供者程序状态。 然后有两种可能的结果:

  • 如果监听提供者程序,则将创建一个新状态。
  • 如果提供者程序未被监听,则提供者程序将被完全处置。
class MyWidget extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
return ElevatedButton(
onPressed: () {
// 点击后,处置提供者程序。
ref.invalidate(someProvider);
},
child: const Text('dispose a provider'),
);
}
}
信息

提供者程序可以使用 ref.invalidateSelf 使自己失效。 尽管在这种情况下,这始终会导致创建新状态。

提示

当尝试使接收参数的提供者程序失效时, 可能会使一个特定的参数组合失效, 也可以同时使所有参数组合失效:


String label(LabelRef ref, String userName) {
return 'Hello $userName';
}

// ...

void onTap() {
// 使该提供者程序所有可能的参数组合无效。
ref.invalidate(labelProvider);
// 仅使特定组合无效
ref.invalidate(labelProvider('John'));
}

使用 ref.keepAlive 微调处置

如上所述,当自动处置启用时,如果在完整的一帧时间里提供者程序没有监听器,状态将被处置。

但您可能希望对此行为有更多的控制权。例如, 您可能希望保留成功网络请求的状态,但不缓存失败的请求。

这可以在启用自动处置后,使用通过 ref.keepAlive 来实现。 使用它,您可以决定何时停止自动处置状态。


Future<String> example(ExampleRef ref) async {
final response = await http.get(Uri.parse('https://example.com'));
// 只有在请求成功完成后,我们才会让提供者程序存活。
// 如果请求失败(并抛出异常),那么当提供者程序停止被监听时,
// 状态就会被处置。
ref.keepAlive();

// 我们可以使用 `link` 恢复自动处置行为:
// link.close();

return response.body;
}
备注

如果重新计算提供者程序,将重新启用自动处置。

也可以使用 ref.keepAlive 的返回值使其恢复到自动处置状态。

示例:在一段特定时间内保持状态

目前,Riverpod 不提供在特定时间内保持状态的内置方法。
但是,使用我们目前看到的工具,实现这样的功能很容易且可重用。

通过使用 Timer + ref.keepAlive,我们可以在特定的时间内保持状态。 为了使这个逻辑可重用,我们可以在扩展方法中实现它:

extension CacheForExtension on AutoDisposeRef<Object?> {
/// 使提供者程序在 [duration] 内存活。
void cacheFor(Duration duration) {
// 立即防止状态遭到破坏。
final link = keepAlive();
// 持续时间结束后,我们将重新启用自动处置功能。
final timer = Timer(duration, link.close);

// 可选项:重新计算提供者程序时(如使用 ref.watch)
// 我们取消待定计时器
onDispose(timer.cancel);
}
}

然后,我们可以这样使用它:


Future<Object> example(ExampleRef ref) async {
/// 保持状态 5 分钟
ref.cacheFor(const Duration(minutes: 5));

return http.get(Uri.https('example.com'));
}

可以调整此逻辑以满足您的需求。 例如,仅当提供者程序在特定时间内未被监听时, 才可以使用 ref.onCancel/ref.onResume 处置状态。