【ProviderとRiverpodの違い⑥】信頼性のあるパラメータ化機構の欠如

【ProviderとRiverpodの違い⑥】信頼性のあるパラメータ化機構の欠如

はじめに

前回に引き続き、今回もRiverpodのモチベーションについて解説していきます。

今回の記事では、「信頼性のあるパラメータ化機構の欠如」というセクションを掘り下げていきます。

信頼性のあるパラメータ化機構の欠如

公式ドキュメントの説明

Riverpod では、.family 修飾子を使用して "外部のパラメータをもとに一意の" provider を宣言できます。

実際、.family は Riverpod の最も強力な機能の 1 つであり、その革新の中心です。

これにより、ロジックの単純化が可能になります。

Provider で同様のことを実装しようとすると、使いやすさとタイプセーフの両方を犠牲にする必要があります。

さらに、Provider では同様の.autoDispose機構を実装できないため、.family同等の実装を妨げることになります。

最後に、前述のように、ウィジェットは InheritedWidget をリッスンするのを決して止めません。

これにより、provider の状態が"動的にマウント"された場合、すなわちパラメータを使用して provider を構築する場合に、重大なメモリリークが発生します。

したがって、Provider に対する.family の同等の実装は現時点では根本的に不可能です。

Providerにおけるパラメータ化されたプロバイダーの課題

Providerを使用してパラメータに基づくプロバイダーを実装するとき、一般的にはファクトリーパターンなどを使用して複数のインスタンスを管理することがあります。

また、状態の自動破棄(autoDispose)のような機能を実装するのは困難で、不要な状態がメモリに残り続けることがあります。

例えば、以下の例では、UserProviderを特定のユーザーIDに基づいて初期化していますが、異なるIDに基づいて複数のUserProviderインスタンスを容易に管理する方法は提供されていません。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class UserProvider with ChangeNotifier {
  final String userId;
  UserProvider(this.userId);

  // User data fetch logic
}

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => UserProvider('user1')),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: Scaffold(body: Text('User Provider Example')));
  }
}

Riverpodの.family修飾子による改善

Riverpodの.family修飾子を使用すると、パラメータに基づいてプロバイダーを動的に生成し、それぞれのインスタンスを自動的に管理することができます。

これにより、ロジックの単純化が可能になり、使いやすさとタイプセーフを保ちながらプロバイダーを柔軟に利用できます。

以下の例では、userProvideruserIdをパラメータとして受け取り、それに基づいてユーザーデータを返します。

.family修飾子により、異なるユーザーIDに対する呼び出しは自動的に管理され、.autoDisposeを使用することで、ウィジェットが不要になったときにプロバイダーの状態が自動的に破棄されます。

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final userProvider = Provider.autoDispose.family<String, String>((ref, userId) {
  // Return user data based on userId
  return "Data for $userId";
});

void main() {
  runApp(
    ProviderScope(
      child: MaterialApp(home: Home()),
    ),
  );
}

class Home extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userData = ref.watch(userProvider('user1'));
    return Scaffold(body: Text(userData));
  }
}

まとめ

Riverpodの.family修飾子は、パラメータ化されたプロバイダーの管理を単純化し、Providerが直面する多くの問題を解決します。これにより開発者は、より少ないコードでより安全かつ効率的に状態管理ができるようになります。

次回、「副作用のトリガーが簡単でない」デュエルスタンバイ!

Anycloudではプロダクト開発の支援を行っています

プロダクト開発をお考えの方はぜひAnycloudにご相談ください。

まずは相談する

記事を書いた人

Matsuura

エンジニア

Matsuura

Twitter

Anycloudでエンジニアしてます!主にFlutterやWebフロントをやっていて、最近はバックエンドやインフラに挑戦・苦戦中。