riverpod-faq-and-practices について
Flutter 開発者向けの Riverpod リファレンススキルです。プロバイダーのライフサイクル、Widget との連携、状態の配置、よくある問題への指針をまとめています。
主な機能
- 01ref.refresh と ref.invalidate の説明
- 02Ref と WidgetRef の使い分け
- 03プロバイダー初期化の実践方法
- 04ローカル状態と共有状態の区別
- 05riverpod_lint の活用指針
活用シーン
- 01ユーザー操作後の更新方法の選択
- 02await 後の安全な ref 使用
- 03状態をプロバイダーに置くかの判断
- 04プロバイダー定義の一般的な問題の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ serverpod/ skills-registry --skill riverpod-faq-and-practicesインストール後、Claude Code で/riverpod-faq-and-practicesを入力して読み込まれたか確認します。
詳細
Riverpod — FAQ とベストプラクティス
FAQ
ref.refresh と ref.invalidate の違い
ref.refresh = ref.invalidate + ref.read:プロバイダーを無効化し、新しい値を返します。新しい値が必要ない場合(例:「ユーザーが引っ張って更新したときに再計算する」)は invalidate を使います。結果がすぐに必要な場合は refresh を使います。invalidate だけを実行した場合、再計算は次のフレーム、またはプロバイダーが次に読み取られたときに行われます。refresh は読み取りを行うことで、即時の再計算を強制します。
Ref と WidgetRef の間に共通のインターフェースがないのはなぜですか?
Ref(プロバイダー内)と WidgetRef(ウィジェット内)は、両方に条件付きで依存するコードを書かないよう、意図的に分けられています。両者には微妙な違いがあり、混在させるとエラーが発生しやすくなります。Ref を優先してください。ロジックを Notifier に置き、UI から ref.read(notifierProvider.notifier).yourMethod() を呼び出します。このメソッドでは Notifier の Ref を使います。
StatelessWidget ではなく ConsumerWidget を使うのはなぜですか?
InheritedWidget(ひいては BuildContext)では、ref.listen のような「変更時」のリスナーをサポートできず、ウィジェットがリスナーを停止するタイミング(auto-dispose や family に必要)も把握できません。また、GlobalKeys を使用する場合など、ライフサイクルに関する癖もあります。これらの機能のために、Riverpod には BuildContext に紐づかない Ref が必要です。そのため、ConsumerWidget(および Consumer)が Ref を提供します。
hooks_riverpod が flutter_hooks をエクスポートしないのはなぜですか?
各パッケージを独立してバージョン管理できるようにするためです。一方の破壊的変更によって、もう一方も変更を余儀なくされることがありません。
すべてのプロバイダーを一度にリセットするには?
すべてのプロバイダーをリセットする API はありません。これはアンチパターンと見なされています。「ログアウトして状態をクリアする」には、現在のユーザーに依存するプロバイダーでユーザープロバイダーを ref.watch します。ユーザーがログアウトすると、そのプロバイダーが変更され、依存するプロバイダーが再計算またはクリアされます。リセットされるのはユーザーに依存する状態だけで、それ以外は保持されます。
「ウィジェットのアンマウント直前またはアンマウント後に ref を使用するのは安全ではありません」
これは(または「No ProviderScope found」は)、破棄される可能性があるウィジェットで await の後に ref を使用すると発生します。await の後は、ref を使用する前に if (!context.mounted) return; を確認してください(BuildContext と同じパターンです)。
やるべきこと/避けるべきこと
ウィジェットでプロバイダーを初期化しない
プロバイダーは自分自身を初期化する必要があります。initState から ref.read(provider).init() のような呼び出しをしないでください。初期化がユーザー操作(例:ナビゲーション)に依存する場合は、その操作から(例:Navigator.push の前に onPressed で)トリガーしてください。
一時的な状態にプロバイダーを使わない
プロバイダーは 共有するビジネス状態 に使い、選択中のタブ/項目、フォームの状態(画面を離れる/戻るときにリセットすべきもの)、アニメーション、コントローラーのような状態(例:TextEditingController)には使わないでください。ウィジェット内のローカルな状態には flutter_hooks またはローカル状態を使います。「選択中の項目」をグローバルプロバイダーに保存すると、戻る操作が壊れることがあります(例:/books/21 から戻ると /books/42 を表示すべきなのに、プロバイダーには 21 が保持されたままになる)。
プロバイダーの初期化中に副作用を実行しない
プロバイダーは「読み取り」操作を表す必要があります。フォームの送信など、「書き込み」操作を実行するためにプロバイダーの build を使わないでください。副作用が実行されなかったり、重複して実行されたりする可能性があります。副作用の読み込み中/エラー状態には mutations(riverpod-mutations を参照)を使います。
ref.watch/read/listen では静的に判明しているプロバイダーを優先する
provider がトップレベルの final(またはその他の方法で静的に判明しているもの)である場合は、ref.watch(provider) を使います。静的解析と riverpod_lint が機能するよう、プロバイダーをパラメーターとして渡してから監視する方法は避けてください。
プロバイダーを動的に作成しない
プロバイダーは トップレベルの final 変数として定義してください。クラス内やインスタンスフィールドとして作成しないでください。メモリーリークやサポートされていない動作につながる可能性があります。クラス内の static final は使用できますが、コード生成ではサポートされていません。
これらのベストプラクティスの多くを徹底するには、riverpod_lint(riverpod-getting-started を参照)を有効にしてください。