riverpod
riverpod copied to clipboard
Documentation doesn't mention eager initialisation of Providers
Describe what scenario you think is uncovered by the existing examples/articles
It seems, providers are initialised lazily only when needed, possibly never. This isn't mentioned in documentation, so I assumed they are created eagerly, specially since they are defined globally. I might be alone in this assumption but I suspect I'm not. At glance it looks like init() method should be called as soon as this file is loaded by Dart VM but it's not:
final notificationProvider = Provider<NotificationProvider>(
(ref) {
return NotificationProvider()..init();
},
);
Describe why existing examples/articles do not cover this case I read through entire documentation (Concepts and Guides, really) and didn't find it mentioned. Also using integrated search to look up "eager" or "lazy" words yielded nothing. I don't think I missed that information.
Global variables are always lazy-loaded. This is a specificity of how Dart behaves.
As a comeback to this, because of #225 – If you want to initialize/read providers inside your main, you can do:
final greeting = Provider<String>((ref) => "hello world");
void main() {
final container = ProviderContainer();
print(container.read(greeting)); // "hello world"
runApp(
UncontrolledProviderScope(
container: container,
child: MyApp(),
),
);
}
can you provide more info on the behavior of "UncontrolledProviderScope"
I'm guessing this tells Riverpod to keep state, not in the widget tree, but in some other area of global memory??
I'm guessing this tells Riverpod to keep state, not in the widget tree, but in some other area of global memory??
No, this stores the state in ProviderContainer
This class is always where the state of your providers is stored.
Using ProviderScope is equivalent to a StatefulWidget that instantiate a ProviderContainer and inserts it in the widget tree with UncontrolledProviderScope
@rrousselGit Can ProviderScope be added anywhere in the tree? instead of as the root widget?
I'm creating an internal package for my team and I'm planning to use riverpod, but not all the apps use riverpod.
Not having ProviderScope as the root of the app is not ideal
if the ProviderScope is somehow destroyed, that'll destroy your app state.
so I'll create a widget that exposes ProviderScope then and have them add it to the root. Would you agree?
Yes
Is this possible to make this into a feature request?
If you wrap something like Firebase/Firestore/Any cloud DB; you sometimes want eager init, to hide server latency.
What I'm asking for is an official way, similar to lazy: false in provider.
It's not doable, because global/static variables in Dart are lazy-loaded.
Providers aren't created until you read them for the first time, so Riverpod can't initialize them even if it wanted to
Maybe have something like:
ProviderScope(
child: myApp(),
eager: [...listOfProviders],
)
All I'm really asking for is some official mechanism to achieve what you demonstrated in https://github.com/rrousselGit/river_pod/issues/202#issuecomment-731585273
The official mechanism to achieve is the official mechanism.
It's not a workaround. That's the actual solution
final greeting = Provider<String>((ref) => "hello world");
void main() {
final container = ProviderContainer();
print(container.read(greeting)); // "hello world"
runApp(
UncontrolledProviderScope(
container: container,
child: MyApp(),
),
);
}
With respect to this^ @rrousselGit, what if we wanted to override values of Providers with this UncontrolledProviderScope as well? ProviderScope allowed overrideWithValue but this does not. What is the workaround in that case?
To give more context, this is related to initialising SharedPreferences in main, where earlier I had
var sharedPreferencesServiceProvider = Provider<SharedPreferenceService>((_) => throw UnimplementedError());
And after getting the instance of SP, I could override the value this way
final sharedPreferences = await SharedPreferences.getInstance();
runApp(
ProviderScope(
overrides: [
sharedPreferencesServiceProvider.overrideWithValue(SharedPreferenceService(sharedPreferences)),
],
child: MyApp(),
),
),
But now if I use UncontrolledProviderScope to solve another problem,
and to override, if I try
container.updateOverrides([
sharedPreferencesServiceProvider.overrideWithValue(SharedPreferenceService(sharedPreferences)),
]);
This ^ does not allow me to override null with ValueProvider<Object?, SharedPreferenceService>.
Actual error: Replaced the override of type Null with an override of type ValueProvider<Object?, SharedPreferenceService>, which is different.
Didn't have this error during ProviderScope overriding. I'm on version 0.14.0+3
With respect to this^ @rrousselGit, what if we wanted to override values of Providers with this UncontrolledProviderScope as well? ProviderScope allowed
overrideWithValuebut this does not. What is the workaround in that case?
The constructor of ProviderContainer has the observers and overrides parameter that you find on ProviderScope. So you can use those
Maybe have something like:
ProviderScope( child: myApp(), eager: [...listOfProviders], )All I'm really asking for is some official mechanism to achieve what you demonstrated in #202 (comment)
This approach would have the benefit of being easier to discover and document, it was also where I first looked for this functionality before I had to go through GitHub issues.
That won't work with family, which is an equally important case.
Surprised me, too. Would be really nice if you can give this a mention.
Global variables are always lazy-loaded. This is a specificity of how Dart behaves.
Yes, but no one here ever even used global variables in Dart before Riverpod 😀
This topic should be addressed in the documentation rework. Maybe inside the "notable Cookbooks examples" section. such guide in particular should be cross-referenced in the "migration from Provider" quickstart.
It does now https://docs-v2.riverpod.dev/docs/essentials/eager_initialization
As a comeback to this, because of #225 – If you want to initialize/read providers inside your main, you can do:
final greeting = Provider<String>((ref) => "hello world"); void main() { final container = ProviderContainer(); print(container.read(greeting)); // "hello world" runApp( UncontrolledProviderScope( container: container, child: MyApp(), ), ); }
Hey @rrousselGit,
so when using this for a StateNotifier and also changing the state with something like container.read(greeting).changeGreeting('hello riverpod')
directly in the main before running the app,
then when using this provider later on in some widget tree and watching its value with something like
Text(ref.watch(greeting))
it will still show the old greeting 'hello world'.
But if I print the new greeting value after setting it to a new one in main, it does give me the updated value. Only the UI seems to miss it.
If I update the state again from a button press, it does work like intended and the text updates to the new value.
Can you explain why this is and if this is intended or how we can fix the UI to be in sync with the provider state from the beginning?
@tobiasbentin I know you didn't ask me, but please read the new documentation, it basically answers your question, but to put it shortly AVOID doing shenanigans with container before ProviderScope. Initialize your data directly into a dedicated pre-app loading page as shown.
@rrousselGit one thing that imo is missing from the page is mentioning how using ref.keepAlive for some one-time-asynchronous singletons is key to avoiding StateErrors when using .requireValue on eagerly initialized providers.
@lucavenir No. That's not an issue with how the page works.
@lucavenir thanks for your answer. of course for this simple case I would do it differently but basically what I wanted to achieve is what is shown in this example but for a StateNotifierProvider instead of a normal Provider. Is there a good way to do that? Currently I am making use of the requireValue getter but I thought it would be nice to have it overwritten so it can be sync.
And I would like to know if using the method described above is fine for initialising stuff like firebase, deeplinks etc.? Just as shown at the end here