Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Everything is provider #586

Merged
merged 219 commits into from
May 1, 2022
Merged

Everything is provider #586

merged 219 commits into from
May 1, 2022

Conversation

bannzai
Copy link
Owner

@bannzai bannzai commented Apr 28, 2022

Abstract

Riverpod をフル活用する方向に修正する。具体的には

  • AsyncValueの採用。AsyncValue を Widget側でも使用して state.when(data:error:loading) の機能を使用する
  • Streamを基本的に使用する。Datastoreに生えていたfetch系のメソッドは基本使わない
    • Streamが統一されることでコードの統一感と、重複して同じ内容のStreamが存在しないようにする
  • StateNotifier内部でStateを作っていたが、外部でStateをProvider経由で用意する。そしてStateNotifierにはinitialStateとして外から渡すようにした。外のProvider(() => State)に変更があればStateNotifier自体も再生成される
  • Datastoreのmutate系のメソッドをAsyncActionとして切り出す。これによりStateNotifierへの不要なDatastoreのDIを無くす。AsyncActionのテストをしたかったら別途AsyncActionのインスタンスを作るだけにする

Code

State

FutureProvider,StreamProviderをwatchして、変更があればStateが更新される。読み込み中はwatchした内容がAsyncLoadingの場合は AsyncLoadingを返す。そうでない場合はvalueを取得する。value取得時にエラーの場合はエラーがthrowされるのでtry,catchをする

final settingStateProvider = Provider<AsyncValue<SettingState>>((ref) {
  final latestPillSheetGroup = ref.watch(latestPillSheetGroupStreamProvider);
  final premiumAndTrial = ref.watch(premiumAndTrialProvider);
  final setting = ref.watch(settingStreamProvider);
  final sharedPreferencesAsyncValue = ref.watch(sharedPreferenceProvider);
  final isHealthDataAvailable = ref.watch(isHealthDataAvailableProvider);

  if (latestPillSheetGroup is AsyncLoading ||
      premiumAndTrial is AsyncLoading ||
      setting is AsyncLoading ||
      sharedPreferencesAsyncValue is AsyncLoading ||
      isHealthDataAvailable is AsyncLoading) {
    return const AsyncValue.loading();
  }

  try {
    final sharedPreferences = sharedPreferencesAsyncValue.value!;
    final userIsMigratedFrom132 =
        sharedPreferences.containsKey(StringKey.salvagedOldStartTakenDate) &&
            sharedPreferences.containsKey(StringKey.salvagedOldLastTakenDate);

    return AsyncValue.data(
      SettingState(
        latestPillSheetGroup: latestPillSheetGroup.value!,
        setting: setting.value!,
        premiumAndTrial: premiumAndTrial.value!,
        isHealthDataAvailable: isHealthDataAvailable.value!,
        userIsUpdatedFrom132: userIsMigratedFrom132,
      )
    );
  } catch (error, stacktrace) {
    return AsyncValue.error(error, stackTrace: stacktrace);
  }
});

StateNotifier

StateNotifierはStateの変更により作り直される。なので、作り直された前と後で変更点以外は生合成が合うように気をつける必要がある

final settingStateNotifierProvider =
    StateNotifierProvider<SettingStateNotifier, AsyncValue<SettingState>>(
  (ref) => SettingStateNotifier(
    asyncAction: ref.watch(settingPageAsyncActionProvider),
    initialState: ref.watch(settingStateProvider),
  ),
);

class SettingStateNotifier extends StateNotifier<AsyncValue<SettingState>> {
  final SettingPageAsyncAction asyncAction;
  SettingStateNotifier({
    required this.asyncAction,
    required AsyncValue<SettingState> initialState,
  }) : super(initialState);
}

Widget

class SettingPage extends HookConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final store = ref.watch(settingStateNotifierProvider.notifier);
    final state = ref.watch(settingStateNotifierProvider);

    useAutomaticKeepAlive(wantKeepAlive: true);

    return state.when(
      data: (state) => SettingPageBody(store: store, state: state),
      error: (error, _) => UniversalErrorPage(
        error: error,
        child: null,
        reload: () => ref.refresh(settingStateProvider),
      ),
      loading: () => ScaffoldIndicator(),
    );
  }
}

Checked

  • Analyticsのログを入れたか
  • 境界値に対してのUnitTestを書いた
  • パターン分岐が発生するWidgetに対してWidgetTestを書いた
  • リリースノートを追加した

@bannzai bannzai marked this pull request as ready for review May 1, 2022 11:22
@bannzai bannzai merged commit 143fb86 into main May 1, 2022
@bannzai bannzai deleted the refactor/state/async-value branch May 1, 2022 11:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant