清除快取並對狀態處置做出反應
到目前為止,我們已經瞭解瞭如何建立/更新某些狀態。 但我們還沒有談論狀態處置何時發生。
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
處置狀態。