こんにちは、とりかつ((@torikatsu923)https://twitter.com/torikatsu923)です。
今朝、riverpodの1.0.0-dev.0が公開されました!
I have published a dev-release of Riverpod v1:
— Remi Rousselet (@remi_rousselet) 2021年6月20日
riverpod 1.0.0-dev.0
Go give it a try 🎉https://t.co/OWGQ6evu4O
以前記事で触れましたが、4月の半ばごろにriverpodの作者の方からriverpodに破壊的変更が入るかもしれないというアナウンスがありました。 torikatsu923.hatenablog.com
Changelogを確認したところ、今回のアップデートはそのアナウンスを受けてのものと考えて良さそうです。 個人的に欲しかった機能がつまっているアップデートなので、使うのがとても楽しみです!
今回のアップデートは変更点が20個程含まれており、破壊的変更も多く含まれています。 pub.dev
そこで今回の記事ではriverpodの変更点についてまとめていきます。
- アップデートの方針の考察
- 変更点一覧
- useProviderの削除
- ConsumerWidgetのbuildメソッドでWidget refを受け取るようにする変更
- ProviderListenerを非推奨にする変更
- ConsumerStatefulWidget, ConsumerStateの追加
- watchでselectでstateの特定の値の変更のみ検知できるようにする変更
- overrideWithValueの削除
- ProviderObserverが変更前と変更後の2種類の状態を受け取れるようにする変更
- ProviderObserver.mayHaveChangedの削除
- ref.listenの追加
- ProviderReferenceを非推奨にする変更
- 全てのProviderがProviderRefBaseをパラメータとして受け取るようにする変更
- ProviderReference.mountedの削除
- ProviderContainerのdebugproviderValuesとdebugProviderElementsの削除
- StreamProviderのlast, stream, futureがプロバイダーの再構築の回数に依存しないようにする変更
- Providerの値が同じ時、Providerをオーバーライドできるようにする変更
- ref.refreshの呼び方変更
- ref.onDisposeのタイミング変更
- Providerの状態の再計算のタイミング変更
- ProviderContainer.pumpの追加
- familyとdisposeを併用した時に状態が残る問題の修正
- おわりに
アップデートの方針の考察
今回のアップデートのキーは「Providerへアクセスする方法を統一する」だと思われます。 初めてriverpodを触る時、Providerをreadする方法の複雑さにウッとなる方も多いと思います。
以前の作者さんの(アナウンス)https://github.com/rrousselGit/river_pod/issues/335や、change logでproviderへのアクセス周りで破壊的変更が多く入っていることから、 アップデートにはProviderへアクセスする方法を統一し、よりシンプルにするにする目的があると考えられます。
実際に変更後はref.read
, ref.watch
, ref.listen
によってProviderへのアクセス、UIでの状態の監視、状態変更の監視が可能になっています。
以下ではこの点を踏まえつつ、変更点を追っていきます。
変更点一覧
useProviderの削除
HookWidgetのuseProvider
がなくなり、Widget ref
を使用してProviderへアクセスするように変更が入っています。
HookConsumerWidget
を継承することで、build
の引数にWidget ref
を受け取ることができるようになります。
Before
class Example extends HookWidget { @override Widget build(BuildContext context) { useState(...); int count = useProvider(counterProvider); ... } }
After
class Example extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { useState(...); int count = ref.watch(counterProvider); ... } }
pub devより引用
ConsumerWidgetのbuildメソッドでWidget refを受け取るようにする変更
従来は以下のようにbuild
メソッドでScopedReader
を受け取っていましたが、変更後はWidgetRef
を受け取るようになりました。
変更後はProviderの変更をwatch
したい際はref.watch
とする必要があります。
Before
class A extends ConsumerWidget { @override Widget build(BuildContext context, ScopedReader watch) { final state = watch(someProvider); } }
After
class A extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final state = ref.watch(someProvider); } }
ProviderListenerを非推奨にする変更
WidgetRef
にlisten
という状態の変更を監視する関数ができました。その関係で、今まで状態の変更を監視する際に使用されていたProviderListener
が非推奨になりました。
この変更により、今までWidgetをネストしなければならなかったところをとてもシンプルに記述することができるようになりました!
Before
class A extends StatelessWidget { @override Widget build(BuildContext context) { return ProviderListener( provider: someProvider, onChange: (context, state, child) { ... }, child: Container( ... ), ); } }
After
class A extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { ref.listen(someProvider, (state) { // something } } }
ConsumerStatefulWidget, ConsumerStateの追加
StatefulWidgetとConsumerWidgetが合体したConsumerStatefulWidget
が追加されました。
StatefulWidgetのライフサイクルにフックしてProviderの処理を走らせたいが、状態の管理はProviderに行わせたいみたいな場合、いままではStatefulWidgetでWidgetを定義して、build内でConsumerで括ってあげる必要がありました。
今回のConsumerStatefulWidgetの追加で、Consumerで括る必要がなくなったため、よりシンプルに記述できるようになりました。
watchでselectでstateの特定の値の変更のみ検知できるようにする変更
userのフィールドにusername
とstatus
があるとします。
以下のコードではstatusに変更があった場合はリビルドされず、usernameに変更があった場合にリビルドされるようになりました。
final userProvider = StateNotifierProvider<UserController, User>(...); Consumer( builder: (context, ref, _) { bool userName = ref.watch(userProvider.select((user) => user.name)); }, )
グローバルな状態とリビルドについて悩んでいたため、この変更は個人的にとても嬉しいです!
overrideWithValueの削除
今まで.family
で作成したProviderはoverride
する必要がありました。その際値のみをオーバーライドするoverrideWithValue
とProviderごとオーバーライドするoverrideWithProvider
の2種類の方法がありました。
しかし、今回の変更でoverrideWithProvider
に一本化されたようです。
ProviderObserverが変更前と変更後の2種類の状態を受け取れるようにする変更
ProviderObserver
が変更の前後の状態を受け取れるように変更が入りました。
これによって状態の差分をみたりする処理が書きやすくなりました。
ProviderObserver.mayHaveChangedの削除
ProviderObserver.mayHaveChanged
が削除されました。
これは破壊的変更ですが、個人的にはあまり使っていない機能だったため影響はありませんでした。
ref.listenの追加
先で触れたようにref.listen
が追加されました。
バージョンアップ後はダイアログの表示等はこれを使用することになります。
ProviderReferenceを非推奨にする変更
ProviderReferenceが非推奨になり、代わりにProviderBaseが採用されました
全てのProviderがProviderRefBaseをパラメータとして受け取るようにする変更
今までProviderは以下のようにref
を受け取っていましたが、これがProviderRefBaseに変更されました。
Provider((ref) => ...);
この変更でrefがstateというプロパティを持つようになり、ref.state++
のようにすることで状態を変更することができるようになりました。
また、StateProviderのrefにはcontrollerというプロパティが追加され、ref.controller....
でStateControllerにアクセスできるようになりました。
ProviderReference.mountedの削除
ProviderReference.mountedが削除されました。代わりにonDisposeを使用することで似たような挙動を実現できるようです。
Provider<T>((ref) { var mounted = true; ref.onDispose(() => mounted = false); });
ProviderContainerのdebugproviderValuesとdebugProviderElementsの削除
ProviderContainerのdebugProviderValuesとdebugProviderElementsが削除され、 ProviderContainer.getAllProviderElementsを使用できるようになりました
StreamProviderのlast, stream, futureがプロバイダーの再構築の回数に依存しないようにする変更
関連するProviderの再構築の回数に関係ないfuture, streamを外部に公開するようになりました。
Providerの値が同じ時、Providerをオーバーライドできるようにする変更
StreamProvider<User>
があった時、Provider<AsyncValue<User>>
でオーバーライドすることができるようになりました。
ref.refreshの呼び方変更
ref.refresh
の呼び方がref.container.refresh
に変更されました。
ref.onDisposeのタイミング変更
Providerの依存関係の変更がわかったらすぐにonDisposeが呼ばれるようになりました。
Providerの状態の再計算のタイミング変更
Providerの依存関係が変更されてリスナがいるとき、次の読み取りまで状態の再計算を待たなくなりました。
ProviderContainer.pumpの追加
ProviderContainer.pumを使うことで、awaitのように簡単にproviderが状態の変更を通知するまで待つことができるようになりました。
familyとdisposeを併用した時に状態が残る問題の修正
familyとdisposeを一緒に使用した際状態が残ってしまう問題がありましたが、今回のアップデートで解決されたようです。
おわりに
今回の記事ではriverpodの変更点を追っていきました。
Providerへのアクセスする際の構文がref
を経由して行われるように統一されたので、より使いやすくなったと感じています!
またいくつかの変更によってシンプルに処理をかけるようになったのもとても嬉しいです!
それではよい開発ライフを!