Closed2
【Riverpod】autoDisposeをしているProviderでref.readをしても大丈夫?

時々、下記のようなrefが破棄されている旨のメッセージが出ませんか?
Cannot use "ref" after the widget was disposed.
上記が出ると「非同期処理でawaitしてる間に破棄されちゃったんだなぁ」くらいの認識でいたのですが、
下記のようなautoDispose
をしているProviderについて、
「ref
はなんで使えるの?disposeされてないの?」と疑問に思い調べたのでメモ程度に記載します。
// autoDisposeなProvider
Repository repository(Ref ref) {
return Repository(ref);
}
class Repository {
Repository(this.ref);
final Ref ref;
Future<HogeFuga> fetch() async {
final hoge = await ref.read(hogeProvider).fetch();
// autoDisposeをしている、
// かつ呼び出し元がref.read(repositoryProvider)をしている関係で、
// この時点ですでにrepositoryProviderはonDisposeが呼ばれている。
//
// このとき、repositoryProviderのrefは破棄されていると判断されないのか疑問に思った。
final fuga = await ref.read(fugaProvider).fetch();
return HogeFuga(hoge, fuga);
}
}
// 呼び出し元
void hogeFunction(WidgetRef ref) async{
final repository = ref.read(repositoryProvider);
final result = await repository.fetch();
}

結論
WidgetRef
とRef
では内部的にProviderContainer
へのアクセス方法が違うので、
Ref
についてはProviderが破棄されていようとref.read
を使用して問題ない。
調査したこと
WidgetRef
とRef
について、ref.read
をしたときの処理の流れを見てみると、
下記の違いがあることがわかりました。
※ProviderContainer
とはプロバイダーの状態を保持する中心的なオブジェクトです。
-
WidgetRef
:ProviderScope.containerOf
で上位にあるProviderContainer
に自分のBuildContext
を使用して遡っているため、WidgetのBuildContext
がmount
しているかをチェックしており、破棄されているとエラーが起きる可能性がある。 -
Ref
:WidgetRef
と同じくProviderContainer
を使用しているが、BuildContext
から読むのではなくRef
(ProviderElementBase
)にて保持しているProviderContainer
を使用するので、特にエラーは起きることがない。
下記に簡易的ですが、処理の流れを記載します。
WidgetRefの場合
下記を呼び出す。
// refは[WidgetRef]
ref.read(hogeProvider);
⇒ ConsumerStatefulElement → read
@override
T read<T>(ProviderListenable<T> provider) {
_assertNotDisposed();
return ProviderScope.containerOf(this, listen: false).read(provider);
}
void _assertNotDisposed() {
// ここでcontextがmountされているかを確認している!
if (!context.mounted) {
throw StateError('Cannot use "ref" after the widget was disposed.');
}
}
⇒ ProviderContainer → read
Result read<Result>(
ProviderListenable<Result> provider,
) {
return provider.read(this);
}
⇒ ProviderBase → read
@override
StateT read(Node node) {
final element = node.readProviderElement(this);
return element.requireState;
}
Refの場合
// refは[Ref]
ref.read(hogeProvider);
⇒ ProviderElementBase → read
@override
T read<T>(ProviderListenable<T> provider) {
_assertNotOutdated();
return _container.read(provider);
}
void _assertNotOutdated() {
// 特にmountされているかどうかはチェックしていない!
assert(
!_didChangeDependency,
'Cannot use ref functions after the dependency of a provider changed but before the provider rebuilt',
);
}
⇒ ProviderContainer → read
Result read<Result>(
ProviderListenable<Result> provider,
) {
return provider.read(this);
}
⇒ ProviderBase → read
@override
StateT read(Node node) {
final element = node.readProviderElement(this);
return element.requireState;
}
このスクラップは3ヶ月前にクローズされました