😽

Riverpod 入門 ~その2~

2024/02/18に公開

自己紹介

  • 受託で働いているしがない 20代Android エンジニア(メンタルやって半日しか働けてない and 結構レガシーコード触ってる)が、VSCode 使って Flutter を今更勉強してみようというやつ
  • Android Studio より VSCode の方が軽いのと fvm とか入れるの便利っぽいので VSCode でやることにしたよ
  • 目標は confference app 2023 を理解するまで週に2,3本書くこと
  • Zenn で収益化?そんなものは知りません!

今日の小ネタ

ガチ小ネタ

稼ぎが少なくてですね、一時避難ということでシェアハウスに住んでるんですよ。
シェアメイトにはモンゴル人とかカナダ人とかアフリカ人がいて多国籍。
文化も価値観も違くて最高ですね😭
カナダ人、お前風呂入れ!💢

Riverpod ってこう言う経験あると理解しやすいんでは?

仕事でコードレビューしてた時にふと思ったこと。
レビュー対象にBaseActivity, BaseFragmentBaseApi があったんですよ。
レガシーコードだから特にリファクタもされてないし影響範囲が広すぎてですね、
手付かずで残っているんですね。

何が辛いって?

  1. オーバーライドされてる関数(onCreate とか)を辿って読みに行くこと
  2. API のハンドリングで onError が2個(共通エラーと個別エラーハンドリングが)ある
  3. 継承先の子クラスで使われてる変数がいつ初期化 or 変更されているのかを突き止めに行くこと
  4. 見なくていいものを見ないといけない、脳のリソース奪われるところ
  5. Base クラスがえげつないコード量の God クラスになってるところ

結論: まあ私の力不足ですけどね、すごい人(給料でダブルスコアつけられてはる)はさっさとリファクタします

こう言うのを経験してると、Riverpod がすごく便利に感じる気がします。
やっていることの本質は、
「如何にイミュータブルな変数を定義して Widget に表示する過程をわかりやすく簡潔に、単純にするか」だからね。
Base~ は個人的に嫌いです。継承も嫌い。委譲はいいんじゃなかろうか。

あと大事なことは2回言います。

概要

今日は前回の続きで Provider について、Riverpod の公式ドキュメントを読み進めていきます
筆者はそもそも Provider is 何?って状況なので丁寧にいくよ!

Let's Start!

  • Providers are the most important part of a Riverpod application.
    だそうです。

Why use providers?

  • Allows easily accessing that state in multiple locations. Providers are a complete replacement for patterns like Singletons, Service Locators, Dependency Injection or InheritedWidgets.
    • なるほど、アクセスが簡単な状態管理シングルトンに代替できる存在だと。
    • DI はわかるけど、InheritedWidgets は使ったことないからわからんね
  • Simplifies combining this state with others. Ever struggled to merge multiple objects into one? This scenario is built directly inside providers.
    • いくつもある状態を統合できるメリットがあるってこと
    • これ自分の経験だと直感的にわからないな、お里が知れそう
  • Enables performance optimizations. Whether for filtering widget rebuilds or for caching expensive state computations; providers ensure that only what is impacted by a state change is recomputed.
    • ref.watch() してる部分のみ再描画(rebuild()) するよってことかな
  • Increases the testability of your application. With providers, you do not need complex setUp/tearDown steps. Furthermore, any provider can be overridden to behave differently during a test, which allows easily testing a very specific behavior.
    • テストコードで必要な初期化処理などを省けて、かつモックできて便利だよと
  • Allows easy integration with advanced features, such as logging or pull-to-refresh.
    • プルリフレッシュとかにも対応するのイージーだよ

小ネタ

dart でシングルトンかくとこんな感じだった

main() {
  
  /// インスタンスによって何も変わってないよね?が分かればいいよ
  var singleton1 = Singleton();
  singleton1.set(key: 'name',data: 'Gaucho');
  singleton1.set(key: 'Academic Background', data: ['中学', '高校', '大学', '大学院']);
  
  var singleton2 = Singleton();
  print(singleton2.get('Academic Background'));
    ///outputs:  [中学, 高校, 大学, 大学院] 出力結果

}
        

class Singleton {
  
  static final Map<String, dynamic> _items = <String,dynamic>{};
  static final Singleton _cash = Singleton._internal();
  
  Singleton._internal();
  
  factory Singleton() {
    return _cash;
  }
  
  set ({required String key,required dynamic data}) => _items[key] = data;
  get (String key) => _items[key];
  
}

Creating a provider


MyValue my(MyRef ref) {
  return MyValue();
}

これで書けるよと。

一本目に書いたブログと重複するけど、まあ以下の点があるよと。

  • グローバル変数っぽいけどイミュータブルだからビビらなくていいよ
  • テストと保守が楽だよ

あと、上のサンプルコードは以下の3点で構成されているよと。

  • final myProvider, the declaration of a variable. This variable is what we will use in the future to read the state of our provider. Providers should always be final.
    • Provider を使用する場合はイミュータブルでなきゃいけないから final つけろって。
  • Provider, the provider that we decided to use. Provider is the most basic of all providers. It exposes an object that never changes. We could replace Provider with other providers like StreamProvider or NotifierProvider, to change how the value is interacted with.
    • 他のプロバイダーって言われても今はわからんな。Stream ってことはあれか、非同期処理かな?
  • A function that creates the shared state. That function will always receive an object called ref as a parameter. This object allows us to read other providers, perform some operations when the state of our provider will be destroyed, and much more.
    • ふーん。refrefference の略語かな多分。ref オブジェクトのおかげで他のプロバイダーの読み込み、状態破棄を検知して色々できるみたい。API を連続で叩く時とかに使うのかな多分🧐
  • The type of the object returned by the function passed to a provider depends on the provider used. For example, the function of a Provider can create any object. On the other hand, StreamProvider's callback will be expected to return a Stream.
    • これはまだちょっとよくわからないな。StreamProvider だから非同期処理する Provider と言うことな気がしているんだが。。

String city(CityRef ref) => 'London';

String country(CountryRef ref) => 'England';

まあ同じ型を返す provider をいくらでも定義することができまっせってことやな。

void main() {
  runApp(ProviderScope(child: MyApp()));
}

runApp のなかで ProviderScope 囲んで初期化しないと Provider 使えませんよと。

Different Types of Providers

お、StreamProvider の話が来そう。

  • やっぱり StreamProvider は API の取得結果を Stream で流す時に使う感じだな
  • Future とかは async, await とかで呼ぶ関数の返り値の型であることは知ってるぞ、dio とか使うんやな
  • Stream についても勉強した方がいいね、実務で使ったことないや
  • NotifierProvider 以下はちょっとよくわからんな

  • ふーん。autoDispose できるってことは AndroidViewModel みがありそうだからやっぱり近いのかも
final userProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
  return fetchUser(userId);
});
  • autoDisposefamilly の併用もできるよって話し。

所感

  • Provider については色々と理解できたけど周辺知識が足りない感じがある
  • Riverpod の使い方、みたいなところをやりたい気持ちがあるけどもう少し Provider に付き合ってみるか
    • と、思って Next を辿って色々見ていったら詳細な説明がめちゃくちゃあったわ
    • これ全部追うまでに1,2ヶ月は軽くかかる
    • getting_started に戻って使い方もやりながらの方がモチベーション続くんでそうする

Next Step

Reading Provider と Combining Provider States くらいまでは頑張って読む!以上!

Discussion