Riverpod: Правильно ли ссылаться в onPressed на провайдера, полученного ранее ref.watch?
Документы Riverpod говорят использовать в обратных вызовах, как кнопкуonPressed
, так как вы не должны использоватьref.watch
там.
Можно ли делать что-то подобное?
build(context, ref) {
// Use ref.watch ahead of time, because I refer to p all over
// the widget tree. p is a mutable ChangeNotifier
final p = ref.watch(myProvider);
return ElevatedButton(
onPressed: () {
// Refer to provider obtained via ref.watch earlier
p.mutateSomehow(),
},
child: /* huge tree referring to p 20 times */
);
}
? Я делаю это во всем своем коде, и, похоже, он работает нормально, но если бы этот шаблон был в порядке, кажется, что Riverpod просто рекомендовал бы его и устарел.ref.read
.
3 ответа
скажем, у вас есть провайдер, определенный следующим образом:final pProvider = StateProvider<int>((ref)=> 0);
и внутри вашей сборки вы смотрите его значение следующим образом:final p = ref.watch(pProvider.state).state;
теперь внутри вашей сборки хранится значение вашего провайдера.
внутри вашего обратного вызова, если вы хотите увеличить значениеp
, вы можете сделать это, просто сказавp++
илиp + 1
, однако это означает, что вы просто увеличиваете полученное значение, а не состояние самого провайдера. это в основном сценарий, когда вы хотите получить значение провайдера, а затем добавить к нему что-то перед сохранением в базе данных.
С другой стороны, если вы хотите изменить состояние провайдера, за которым наблюдает вашref.watch
метод, то вам нужно увеличить свой поставщик состояния, как этоref.read(pProvider.notifier).state++
это гарантирует, что то, что вы увеличиваете, является состоянием поставщика, а не сохраненным значением. и это также вызывает перестройку вашего виджета, потому что отслеживаемое состояние меняется.
Надеюсь, это поможет объяснить вашу озабоченность, основываясь на том, что я понял.
Я думаю, это сделано для того, чтобы ГАРАНТИРОВАТЬ , что мы получаем самый актуальный статус провайдера. Поэтому следует использовать
И вот полный пример возможной ситуации:
void main() => runApp(const ProviderScope(child: MyApp()));
final indexProvider = StateProvider<int>((ref) {
ref.listenSelf((previous, next) {
print(ref.controller.state);
});
return 0;
});
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
print('build $MyApp');
return MaterialApp(
home: Center(
child: Column(
children: const [
SizedBox(height: 50.0),
ButtonInternalState(),
SizedBox(height: 50.0),
ButtonProviderState(),
],
),
),
);
}
}
class ButtonInternalState extends ConsumerWidget {
const ButtonInternalState({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
print('build $ButtonInternalState');
// Once upon a time, all we had to do was read the data once and not keep track of anything
final StateController<int> indexState = ref.read(indexProvider.state);
return ElevatedButton(
onPressed: () => indexState.state++,
// It would have avoided the mistake
// onPressed: () => ref.read(indexProvider.state).state++,
child: Text('The state our wrong provider - ${indexState.state}'),
);
}
}
class ButtonProviderState extends ConsumerWidget {
const ButtonProviderState({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
print('build $ButtonProviderState');
final int index = ref.watch(indexProvider);
return Column(
children: [
ElevatedButton(
onPressed: () => ref.read(indexProvider.notifier).state++,
child: Text('Here changes state the provider - $index')),
ElevatedButton(
onPressed: () => ref.refresh(indexProvider),
child: const Text('Refresh our provider')),
],
);
}
}
и отличаются:
Мы используемref.read
чтобы получить значение поставщика один раз (одноразовое чтение).
Мы используемref.watch
чтобы получить значение поставщика в первый раз и каждый раз, когда значение изменяется (наблюдайте за этим, как если бы вы подписались на поставщика, поэтому вы будете уведомлены об изменениях в любое время).
Вам не нужно всегда получать значение, вы должны получить значение поставщика только один раз.
Обновлять:
Я предлагаю вам использоватьp
в другом месте и использоватьref.watch(myProvider).doSomething()
вonPressed
функция.