StateNotifier は推奨されなくなった!
対象者
- riverpod使ったことがある人
- StateNotifier使ったことがある人
- riverpod2.0使ったことがある人
プロジェクトの説明
Riverpod 2.0 とともに、新しいクラスが導入されました: Notifier / AsyncNotifer。
StateNotifier は、これらの新しい API を優先して非推奨になりました。
このページでは、非推奨の StateNotifier から新しい API に移行する方法を説明します。
AsyncNotifier によって導入された主な利点は、より優れた非同期サポートです。実際、AsyncNotifier は、UI から変更する方法を公開できる FutureProvider と考えることができます。
さらに、新しい (非同期)Notifiers は、次の機能を備えています:
- クラス内で Ref オブジェクトを公開
- コード生成アプローチと非コード生成アプローチの間で同様の構文を提供
- 同期バージョンと非同期バージョンの間で同様の構文を提供
- ロジックをプロバイダーから移動し、Notifiers 自体に集中化
なんてこった非推奨になってしまったか😇
移行するしかないか...
Old Code
昔からあるStateNotifierですね。 Ref, late, dispose etc.......
たくさん書いてあるな😅
Riverpod2.0からはもっと短く書けるらしい?
タイマー作るよ
タイマー作って古いコードと新しいコードの比較してみますか。
[Repository]
class Repository {
Future<void> update(int value) async {
await Future.delayed(const Duration(seconds: 1));
}
}
[StateNotifier]
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:provider_tutorial/application/repository/repository.dart';
final repositoryProvider = Provider<Repository>((ref) {
return Repository();
});
final durationProvider = Provider<Duration>((ref) {
return const Duration(seconds: 1);
});
class MyNotifier extends StateNotifier<int> {
MyNotifier(this.ref, this.period) : super(0) {
// 1 init logic
_timer = Timer.periodic(period, (t) => update()); // 2 side effect on init
}
final Duration period;
final Ref ref;
late final Timer _timer;
Future<void> update() async {
await ref.read(repositoryProvider).update(state + 1); // 3 mutation
if (mounted) state++; // 4 check for mounted props
}
Future<void> reset() async {
await ref.read(repositoryProvider).update(0); // 3 mutation
if (mounted) state = 0; // 4 check for mounted props
}
void dispose() {
_timer.cancel(); // 5 custom dispose logic
super.dispose();
}
}
final myNotifierProvider = StateNotifierProvider<MyNotifier, int>((ref) {
// 6 provider definition
final period = ref.watch(durationProvider); // 7 reactive dependency logic
return MyNotifier(ref, period); // 8 pipe down `ref`
});
New Code
コードを見てみると、コンストラクターに初期化された時の処理を書いていたのが、build
メソッドの中に移動してる。dispose
メソッドがなくなって、代わりに ref.onDispose
になってますね。これでタイマーキャンセルして、状態 (state)を破棄するのかな。
import 'dart:async';
import 'package:provider_tutorial/application/repository/repository.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'my_notifier.g.dart';
(keepAlive: true)
Repository repository(RepositoryRef ref) {
return Repository();
}
Duration duration(DurationRef ref) {
return const Duration(seconds: 1);
}
class MyNotifier extends _$MyNotifier {
int build() {
// Just read/write the code here, in one place
final period = ref.watch(durationProvider);
final timer = Timer.periodic(period, (t) => update());
ref.onDispose(timer.cancel);
return 0;
}
Future<void> update() async {
await ref.read(repositoryProvider).update(state + 1);
// `mounted` is no more!
state++; // This might throw.
}
Future<void> reset() async {
await ref.read(repositoryProvider).update(0);
state = 0;
}
}
[Viewに表示してみるか...]
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:provider_tutorial/application/new/my_notifier.dart';
import 'package:provider_tutorial/presentation/theme/theme_color.dart';
class MyView extends ConsumerWidget {
const MyView({super.key});
Widget build(BuildContext context, WidgetRef ref) {
final myNotifier = ref.watch(myNotifierProvider);
return Scaffold(
appBar: AppBar(
backgroundColor: ThemeColor.green,
title: const Text('Bugs Encountered'),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(myNotifierProvider.notifier).update(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$myNotifier',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
ElevatedButton(
onPressed: () async {
await ref.read(myNotifierProvider.notifier).reset();
},
child: const Text('Reset')),
],
),
),
);
}
}
こちらのデモアプリですが、タイマーが止まりません笑
停止ボタン押しても止まらないので削除することをオススメします。なぜだろうか....
感想
Riverpod2.0から使える新しいNotifier
クラスは、コードの記述量の減少以外にもコードの自動生成のAPIを提供してくれるので開発を楽にしてくれるようですね。古いコードの知識もないと、メリットも仕組みもわからないですが😅
筆者は、去年まだレガシーなriverpodのコードを書くプロジェクトにアサインされたことがあるので、古いコードの使いずらさを知っております。勉強にはなったが...
新しいバージョンのメリットは・:*+.(( °ω° ))/.:+
クラス内で Ref オブジェクトを公開
Refメソッド書いてなかったですね笑
コード生成アプローチと非コード生成アプローチの間で同様の構文を提供
自動生成しないコードもあるな。
Discussion