パルカワ2

最近はFlutterをやっています

なぜRiverpodのFutureProviderを推すのか

RiverpodにはFutureProviderというのがある。

FutureProvider class - riverpod library - Dart API

非常によく出来たAPIだなぁと思ったのでそれのメリット・デメリットと所感をまとめたい。間違えているとかなにかあれば教えてください。

まずFutureProviderの例

コードは、公式ドキュメントから引用している。

FutureProvider class - riverpod library - Dart API

設定ファイルからJSONを取得してDartのObjectに変換して返すという処理があるとする。設定ファイルからJSONを取得部分がasync/awaitに依存している。

Provider

FutureProviderは値を取得して返す処理が書かれている。 Configurationは1度取得したら明示的にconfigProvider に対して reloadしたりするまで設定ファイルが再度読まれることはない。

final configProvider = FutureProvider<Configuration>((ref) async {
  final content = json.decode(
    await rootBundle.loadString('assets/configurations.json'),
  ) as Map<String, Object?>;

  return Configuration.fromJson(content);
});

Widget

FutureProviderをwatchするとAsyncValueが返ってくる。Futureを使うということは、loading, error, data(データ取得成功)の状態があるので、それぞれの状態にあったWidgetを返す。

Widget build(BuildContext, ScopedReader watch) {
  AsyncValue<Configuration> config = watch(configProvider);

  return config.when(
    loading: () => const CircularProgressIndicator(),
    error: (err, stack) => Text('Error: $err'),
    data: (config) {
      return Text(config.host);
    },
  );
}

FutureProviderはどういうことができるのか

アプリを作るときによくある「値をアプリ外から取得する」「取得中などの状態管理をする」「取得した値をStateとして持つ」ことができる。もちろんRiverpodのProviderとして持つので、値が更新されたら再描画などができる。

具体的にはこういうの

  • 設定ファイルから読み込んでStateとして持ちたい
  • Web APIを叩いて値をStateとして持ちたい

ちなみにReact界隈では"Application Stateと呼んでいるものの多くは、実際にはサーバーにあるStateをクライアント側でキャッシュしているに過ぎない"みたいな話があるらしくまぁ確かにと思いながらこのツイートのツリーを読んでいた。

FutureProviderのメリット

  • initStateなどで最初の読み込み処理を呼ぶ必要がない
  • 依存するデータが更新されたときに値の再生成が自動的に行われる
  • AsyncValueが返ってくる形になっているのでloading, error, dataなどの状態管理が必ず行われる
  • loading, errorなどの状態管理を自分でする必要がない

FutureProviderのデメリット

  • initStateなどで明示的に呼び出す形ではないので、どのタイミングでAPIが呼び出されるか慣れていないのわからないことがある

所感

Riverpodは、値Aが更新されたら値Aに依存する画面を再描画したり、値Aに依存する値の更新が行われる。 同様にWeb APIを叩く時に値Aに依存している場合、値Aが更新されたらWeb APIも再度叩いてほしい場合などがある。具体的には、FutureProviderがAPIClientに依存していてAPIのエンドポイントが変わってAPIClientが更新された時など。

initStateなどで読み込む場合はそういうのに対応するには自前で書く必要があるが、Riverpodにまかせてしまうとそれが不要になる。

明示的にAPIを呼び出さないので混乱のもとになるような気もしたけど、他の値同様に依存している値が更新されたら更新されるという形になるので設計的にも統一感があってシンプルになると思う。

またFutureを使うということは、loading, error, sccuessという状態管理を必ず行う必要がある。FutureProviderを使えば自分たちでisLoadingとかを管理していたのが不要になる。

このような理由から個人的には値を取得する部分ではFutureProviderを使うのがよいと感じている。

関連

この記事を書くにあたって読んだ記事など。SWRやreact-queryは関連している気がしたので読んだ。参考になりました。ありがとうございます!