コード生成について
コード生成、ツールを使用してコードを自動生成するアイデアです。
Dart では、アプリケーションを"コンパイル"する際に追加のステップが必要です。
この問題は、近い将来、Dart チームがこの問題に対処しようとしているため、解決される可能性があります。
Riverpod では、コード生成は"provider"を定義するための構文を変更することを意味します。
以下が例です:
final fetchUserProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
});
コード生成を使用すると、次のように書くことができます:
Future<User> fetchUser(FetchUserRef ref, {required int userId}) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
}
Riverpod を使う時、コード生成は完全にオプションです。
Riverpod を使用するために必須ではありません。
ただし、Riverpod はコード生成をサポートしており、使用を推奨しています。
Riverpod のコード生成のインストール方法と使用方法については、 開始方法ページを参照してください。
ドキュメントのサイドバーでコード生成を有効にすることを忘れないでください。
コード生成を使用すべきか?
Riverpod ではコード生成はオプションです。
その点を考慮すると、使用すべきかどうか疑問に思うかもしれません。
答えは:使用した方が良いです。
コード生成を使うことは Riverpod を使用する上で推奨される方法です。
これは将来性があり、Riverpod を最大限に活用することができます。
多くのアプリケーションがすでにFreezed や json_serializableのパッケージでコード生成を使用しています。
その場合、プロジェクトはすでにコード生成の設定がされており、Riverpod を使用することは簡単です。
現在、コード生成はがオプションなのはbuild_runner
が多くの人に好まれないためです。
しかし、Static Metaprogrammingが Dart で利用可能になると、
build_runner
はもはや問題ではありません。
その時点で、コード生成を使用することが Riverpod で唯一の方法になるでしょう。
build_runner
を使用することが大きな問題であれば、その場合のみコード生成を使用しないことを検討してください。
ただし、その場合、一部の機能が使用できなくなり、将来的にコード生成に移行する必要があります。
その際、Riverpod はマイグレーションをスムーズに進めるためのツールを提供します。
コード生成を使用する利点は何?
"Riverpod でコード生成がオプションなら、なぜ使用するのか?"と思うかもしれません。
いつものように、パッケージを使う理由は:開発を楽にするためです。
これには以下が含まれますが、限定されるものではありません:
- より良い構文、より読みやすく柔軟性があり、学習曲線が低い。
- provider のタイプを気にする必要はありません。
ロジックを書き、Riverpod が最も適した provider を選択します。 - 構文は"よくないグローバル変数"を定義しているようには見えません。代わりにカスタム関数/クラスを定義します。
- provider にパラメータを渡すことが制限されなくなりました。.familyを使用して単一の位置パラメータを渡すことを制限される代わりに、任意のパラメータを渡すことができます。 これには名前付きパラメータ、オプション、さらにはデフォルト値も含まれます。
- provider のタイプを気にする必要はありません。
- ステートフルなホットリロード が Riverpod で書かれたコードに適用されます。
- 追加のメタデータを生成し、それをデバッガーが拾い上げることで、より良いデバッグが可能になります。
- いくつかの Riverpod の機能はコード生成でのみ利用可能になります。
構文
provider の定義:
コード生成を使って provider を定義する時、以下のようなことに注意してください:
- provider は注釈付きの関数または注釈付きのクラスとして定義できます。
どちらもほぼ同じですが、クラスベースの provider は公開メソッドを含む利点があり、外部のオブジェクトが provider の状態を変更することができます(副作用)。
関数ベースの provider は、
build
メソッドだけを持つクラスベースの provider を記述するためのシンタックスシュガーであり、UI によって変更することはできません。 - 全ての Dart のasyncプリミティブ(Future、FutureOr、および Stream)がサポートされています。
- 関数が asyncとしてマークされると、プロバイダーは自動的にエラー/読み込み状態を処理し、AsyncValue を公開します。
Functional (publicメソッドを使用して副作用を実行できません。) | Class-Based (publicメソッドを使用して副作用を実行できます。) | |
---|---|---|
Sync |
|
|
Async - Future |
|
|
Async - Stream |
|
|
autoDispose の有効/無効化:
コード生成を使う時、provider はデフォルトで自動破棄されます。
これは、リスナーがアタッチされていないときに自動的に破棄されることを意味します(ref.watch/ref.listen)。
このデフォルト設定は、Riverpod の理念によりよく一致しています。
非コード生成バリアントでは、package:provider
から移行するユーザーに対応するため、autoDispose はデフォルトで無効になっていました。
自動破棄を無効にしたい場合は、keepAlive: true
アノテーションを記載することで可能です。
// AutoDispose provider (keepAlive is false by default)
String example1(Example1Ref ref) => 'foo';
// Non autoDispose provider
(keepAlive: true)
String example2(Example2Ref ref) => 'foo';
provider にパラメータを渡す (family):
コード生成を使用する場合、provider にパラメータを渡すためには、family
修飾子を使用する必要はありません。
代わりに、provider のメイン関数は、名前付きパラメータ、オプションのパラメータ、デフォルト値を含む任意の数のパラメータを受け取ることができます。
ただし、これらのパラメータは依然として一貫性のある==
を持つ必要があることに注意してください。
つまり、値はキャッシュされるか、パラメータは==
をオーバーライドする必要があります。
Functional | Class-Based |
---|---|
|
|
非コード生成バリアントからの移行:
非コード生成バリアントを使用する場合、provider の型を直接指定する必要があります。
以下は、コード生成に移行するための対応オプションです:
Provider | |
Before |
|
After |
|
NotifierProvider | |
Before |
|
After |
|
FutureProvider | |
Before |
|
After |
|
StreamProvider | |
Before |
|
After |
|
AsyncNotifierProvider | |
Before |
|
After |
|
StreamNotifierProvider | |
Before |
|
After |
|