【ProviderとRiverpodの違い⑤】状態の破棄が難しい
はじめに
前回に引き続き、今回もRiverpodのモチベーションについて解説していきます。
今回の記事では、「状態の破棄が難しい」というセクションを掘り下げていきます。
Providerは状態の破棄が難しい
公式ドキュメントの説明
InheritedWidget
は、consumer が listen を停止したときに反応できません。 このため、Provider は provider の状態を自動的に破棄することができません。Provider では、状態が使用されなくなったときにそれを破棄するために provider をスコープする必要がありますが、 これはページ間で状態を共有する場合に特に難しくなります。
Riverpod は、autodisposeやkeepAliveなどの理解しやすい API を提供し、この問題を解決します。
これらの API は、時間ベースのキャッシングなど、柔軟でクリエイティブなキャッシング戦略を可能にします:
// コード生成を使用すると、.autoDisposeがデフォルトになります。
@riverpod
int diceRoll(DiceRollRef ref) {
// このproviderは.autoDisposeであるため、,
// 監視を解除すると現在公開されている状態が破棄されます。
// その後、このproviderが再び監視されると、
// 新しいdiceが振られ再び公開されます。
final dice = Random().nextInt(10);
return dice;
}
@riverpod
int cachedDiceRoll(CachedDiceRollRef ref) {
final coin = Random().nextInt(10);
if (coin > 5) throw Exception('Way too large.');
// 上記の条件が失敗する可能性があります。
// そうでない場合、以下の指示により、誰もリスニングしていなくても
// providerにキャッシュされた状態を維持させます。
ref.keepAlive();
return coin;
}
残念ながら、これは生の
InheritedWidget
では実装できないため、Provider でも実現できません。
Providerにおける状態の破棄の課題
Provider では、InheritedWidget
に基づいて構築されており、ウィジェットツリーにおいてデータの消費者(consumers)がそのリスニングを停止した際に、プロバイダーがその変化に対応することが困難です。
これは、Providerの状態が、ウィジェットツリーから削除された時に自動的に破棄されないため、不要なメモリ保持を引き起こす可能性があります。
ここでは具体的なコードは出さないですが、特定の状態をページ間で共有しつつ、そのページがポップされた時に適切に状態をクリーンアップしない場合にページが消えた後も状態がメモリに残り続けることがあります。
Riverpodにおける状態の改善
Riverpod では、 .autoDispose
修飾子と ref.keepAlive()
メソッドを通じて、状態のライフサイクル管理を細かくコントロールすることができます。
autoDispose
プロバイダーが画面上で必要とされない時(つまり監視しているウィジェットがすべて破棄された時)に、プロバイダーの状態を自動的に破棄します。
これにより、不要なリソースの消費を防ぎ、メモリリークのリスクを減らすことができます。
以下実際の用途としてイメージが湧きやすい参考サイトを貼っておきます。
keepAlive
プロバイダーの状態をアクティブに保つために使用されます。
これにより、特定の条件下で状態を意図的に保持することが可能になり、例えばバックグラウンドでのデータフェッチや長時間にわたる計算が必要な場合に有効です。
実際の例として以下コードでは、リクエストが失敗し、ユーザーが画面を離れてから再度画面に入ると、リクエストが再度実行されます。
ただし、リクエストが正常に完了した場合、状態は保持され、画面に再度入っても新しいリクエストはトリガーされません。
final myProvider = FutureProvider.autoDispose((ref) async {
final response = await httpClient.get(...);
ref.keepAlive();
return response;
});
まとめ
状態管理ライブラリがプロバイダーを生成巣るタイミング、アップデートするタイミング、そして必要がなくなったときにどのように破棄されるかを厳格に制御することにより、アプリケーションのパフォーマンスが向上し、メモリリークなどの問題を避けることができます。
次回、「信頼性のあるパラメータ化機構の欠如」デュエルスタンバイ!