mirror of
https://github.com/rrousselGit/riverpod.git
synced 2025-08-15 10:11:27 +08:00
159 lines
5.5 KiB
Plaintext
159 lines
5.5 KiB
Plaintext
---
|
|
title: FAQ
|
|
version: 2
|
|
---
|
|
|
|
import { Link } from "/src/components/Link";
|
|
import { AutoSnippet, When } from "/src/components/CodeSnippet";
|
|
|
|
Here are some commonly asked questions from the community:
|
|
|
|
## What is the difference between `ref.refresh` and `ref.invalidate`?
|
|
|
|
You may have noticed that `ref` has two methods to force a provider to recompute,
|
|
and wonder how they differ.
|
|
|
|
It's simpler than you think: `ref.refresh` is nothing but syntax sugar for
|
|
`invalidate` + `read`:
|
|
|
|
```dart
|
|
T refresh<T>(provider) {
|
|
invalidate(provider);
|
|
return read(provider);
|
|
}
|
|
```
|
|
|
|
If you do not care about the new value of a provider after recomputing it,
|
|
then `invalidate` is the way to go.
|
|
If you do, use `refresh` instead.
|
|
|
|
:::info
|
|
This logic is automatically enforced through lint rules.
|
|
If you tried to use `ref.refresh` without using the returned value,
|
|
you would get a warning.
|
|
:::
|
|
|
|
The main difference in behavior is by reading the provider right
|
|
after invalidating it, the provider **immediately** recomputes.
|
|
Whereas if we call `invalidate` but don't read it right after,
|
|
then the update will trigger _later_.
|
|
|
|
That "later" update is generally at the start of the next frame.
|
|
Yet, if a provider that is currently not being listened to is invalidated,
|
|
it will not be updated until it is listened to again.
|
|
|
|
## Why is there no shared interface between Ref and WidgetRef?
|
|
|
|
Riverpod voluntarily dissociates `Ref` and `WidgetRef`.
|
|
This is done on purpose to avoid writing code which conditionally
|
|
depends on one or the other.
|
|
|
|
One issue is that `Ref` and `WidgetRef`, although similar looking,
|
|
have subtle differences.
|
|
Code relying on both would be unreliable in ways that are difficult to spot.
|
|
|
|
At the same time, relying on `WidgetRef` is equivalent to relying on `BuildContext`.
|
|
It is effectively putting your logic in the UI layer, which is not recommended.
|
|
|
|
---
|
|
|
|
Such code should be refactored to **always** use `Ref`.
|
|
|
|
The solution to this problem is typically to move your logic
|
|
into a `Notifier` (see <Link documentID="essentials/side_effects" />),
|
|
and then have your logic be a method of that `Notifier`.
|
|
|
|
This way, when your widgets want to invoke this logic, they can
|
|
write something along the lines of:
|
|
|
|
```dart
|
|
ref.read(yourNotifierProvider.notifier).yourMethod();
|
|
```
|
|
|
|
`yourMethod` would use the `Notifier`'s `Ref` to interact with other providers.
|
|
|
|
## Why do we need to extend ConsumerWidget instead of using the raw StatelessWidget?
|
|
|
|
This is due to an unfortunate limitation in the API of `InheritedWidget`.
|
|
|
|
There are a few problems:
|
|
|
|
- It is not possible to implement an "on change" listener with `InheritedWidget`.
|
|
That means that something such as `ref.listen` cannot be used with `BuildContext`.
|
|
|
|
`State.didChangeDependencies` is the closest thing to it, but it is not reliable.
|
|
One issue is that the life-cycle can be triggered even if no dependency changed,
|
|
especially if your widget tree uses GlobalKeys (and some Flutter widgets already do so internally).
|
|
|
|
- Widgets listening to an `InheritedWidget` never stop listening to it.
|
|
This is usually fine for pure metadata, such as "theme" or "media query".
|
|
|
|
For business logic, this is a problem.
|
|
Say you use a provider to represent a paginated API.
|
|
When the page offset changes, you wouldn't want your widget to keep listening
|
|
to the previously visible pages.
|
|
|
|
- `InheritedWidget` has no way to track when widgets stop listening to them.
|
|
Riverpod sometimes relies on tracking whether or not a provider is being listened to.
|
|
|
|
This functionality is crucial for both the auto dispose mechanism and the ability to
|
|
pass arguments to providers.
|
|
Those features are what make Riverpod so powerful.
|
|
|
|
Maybe in a distant future, those issues will be fixed. In that case,
|
|
Riverpod would migrate to using `BuildContext` instead of `Ref`.
|
|
This would enable using `StatelessWidget` instead of `ConsumerWidget`.
|
|
But that's for another time!
|
|
|
|
## Why doesn't hooks_riverpod exports flutter_hooks?
|
|
|
|
This is to respect good versioning practices.
|
|
|
|
While you cannot use `hooks_riverpod` without `flutter_hooks`,
|
|
both packages are versioned independently. A breaking change could happen
|
|
in one but not the other.
|
|
|
|
## Is there a way to reset all providers at once?
|
|
|
|
No, there is no way to reset all providers at once.
|
|
|
|
This is on purpose, as it is considered an anti-pattern. Resetting all providers
|
|
at once will often reset providers that you did not intend to reset.
|
|
|
|
This is commonly asked by users who want to reset the state of their application
|
|
when the user logs out.
|
|
If this is what you are after, you should instead have everything dependent on the
|
|
user's state to `ref.watch` the "user" provider.
|
|
|
|
Then, when the user logs out, all providers depending on it would automatically
|
|
be reset but everything else would remain untouched.
|
|
|
|
## I have the error "Using "ref" when a widget is about to or has been unmounted is unsafe." after the widget was disposed", what's wrong?
|
|
|
|
You might also see "Bad state: No ProviderScope found", which is an older
|
|
error message of the same issue.
|
|
|
|
This error happens when you try to use `ref` in a widget that is no longer
|
|
mounted. This generally happens after an `await`:
|
|
|
|
```dart
|
|
ElevatedButton(
|
|
onPressed: () async {
|
|
await future;
|
|
ref.read(...); // May throw "Using "ref" when a widget is about to or has been unmounted is unsafe."
|
|
}
|
|
)
|
|
```
|
|
|
|
The solution is to, like with `BuildContext`, check `mounted` before using `ref`:
|
|
|
|
```dart
|
|
ElevatedButton(
|
|
onPressed: () async {
|
|
await future;
|
|
if (!context.mounted) return;
|
|
ref.read(...); // No longer throws
|
|
}
|
|
)
|
|
```
|