🧘‍♂️

[Riverpod]AsyncNotifierの正しいState更新方法

2023/05/07に公開

Remi Rousseletさんがupdate method正しい使い方を紹介していたため、備忘録として残しておきます。

https://github.com/rrousselGit/riverpod/issues/2205

AsyncNotifierとは

Riverpod(Flutterの状態管理ライブラリ)における、非同期処理を管理するための基底クラスです。
StateAsyncValueでラップし、

の3つの状態でStateを管理します。

AsyncValueとは

AsyncValueは、

  • AsyncLoading(): 値がまだ利用可能でないことを示す「ロード中」の状態
  • AsyncData(): 値が正常に読み込まれたことを示す「完了」の状態
  • AsyncError():エラーが発生したことを示す「エラー」の状態

のいずれかを持つことができ、状態に応じて処理を分岐させるメソッドを提供しています。

詳しいことは以下の記事を参照してください。
https://zenn.dev/tsuruo/articles/52f62fc78df6d5

update methodとは

AsyncNotifierで管理されているStateを更新するためのメソッドです。
このメソッドは、AsyncValueを更新及びエラーハンドリングを一括で宣言することができます。

update((data) {
  state = const AsyncLoading();
  // 更新処理
}, onError: (error, stack) {
  state = AsyncError(error, stack);
  // エラーハンドリング
}

update methodの正しい使い方

  /// ケースA: アンチパターン
  /// Stateが常時[AsyncLoading()]になるため、「非同期処理が永遠に続いている」状態になる
  void caseA() {
    state = const AsyncLoading();
    update((data) => data = 2);
  }

  /// ケースB: 良い実装
  /// Stateに[AsyncLoading()]を代入し、非同期処理が開始します。その後、処理の結果が[AsyncData()]もしくは[AsyncError()]としてStateに代入される
  void caseB() {
    update((data) {
      state = const AsyncLoading();
      return data += 1;
    });
  }

  /// ケースC: 非推奨の実装
  /// Stateに[AsyncData()]もしくは[AsyncError()]の値が代入される。だが、[AsyncLoading()]にならないため、「非同期処理を実行中」の状態を把握しておらず、状態管理として不十分。
  void caseC() {
    update((unused) => unused += 1);
  }

上記3種類の実装例が考えられるが、caseBの実装方法が非同期の状態を管理する方法として最適である。

caseA

update内での処理が反映されず、StateAsyncLoading()の状態で更新されない状態になります。

caseB & caseC

update内での処理が反映され、StateAsyncData()(もしくはAsyncError())が代入され値が更新されます。
caseCの場合、AsyncLoading()の状態を管理できないため不十分な実装になります。

Stateの更新方法使い分け

/// [update method]を使ったケース
update((data) {
  state = const AsyncLoading();
  return data += 1;
});

---

/// [AsyncValue]を使ったケース
state = const AsyncLoading();
state = AsyncValue.guard(() {
  return state.when(data);
})

update method「このAsyncNotifierで管理されている情報を更新しているぞ!」 と明示的に表現することができるため、AsynNotifierStateのみ変更するユースケースでは積極的に使いますが、

  • 別Stateの更新
  • 他Providerを参照し、Stateを更新する

など更新処理が複雑だと可読性が低下し、エラーハンドリングが困難になります。これらの問題を回避するため、AsyncValueを使ったケースを使用します。

GitHubで編集を提案

Discussion