跳到主要內容

清除快取並對狀態處置做出反應

到目前為止,我們已經瞭解瞭如何建立/更新某些狀態。 但我們還沒有談論狀態處置何時發生。

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.onResumeonCancel 呼叫之後新增新的監聽器時呼叫。
資訊

您可以根據需要多次呼叫 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 處置狀態。