Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should didUpdateProvider receive an AsyncError while providerDidFail can handle it now? #3146

Open
AhmedLSayed9 opened this issue Nov 21, 2023 · 4 comments
Assignees
Labels
breaking enhancement New feature or request
Milestone

Comments

@AhmedLSayed9
Copy link
Contributor

ProviderObserver.providerDidFail now handles Exceptions from asynchronous providers (#3067) but didUpdateProvider still receive AsyncErrors too, which might lead to issues/duplicates. Is there a reason for that?

I'd like to fix this if it's not intended.

@rrousselGit
Copy link
Owner

That's consistent with how the observer works.
If the provider throws synchronously on creation, both providerDidFail and didAddProvider would be invoked.

Changing it would've been breaking anyway. So I don't think it's worth changing

@rrousselGit rrousselGit added enhancement New feature or request breaking and removed bug Something isn't working needs triage labels Nov 21, 2023
@AhmedLSayed9
Copy link
Contributor Author

If the provider throws synchronously on creation, both providerDidFail and didAddProvider would be invoked.

I think if didUpdateProvider won't receive AsyncErrors, this should be updated too so only providerDidFail would be invoked.

The common use case for ProviderObserver is to log states and report errors. With the current behavior we have to do:

class ProviderLogger extends ProviderObserver {
  @override
  void didAddProvider(
    ProviderBase<Object?> provider,
    Object? value,
    ProviderContainer container,
  ) {
    // We'll have to add this to avoid duplicate logs or other logic execution.
    if (value is Exception) return;

    log('TODO: log provider and the state');
  }

  @override
  void didUpdateProvider(
    ProviderBase<dynamic> provider,
    Object? previousValue,
    Object? newValue,
    ProviderContainer container,
  ) {
    // We'll have to add this to avoid duplicate logs or other logic execution.
    if (newValue is AsyncError) return;

    log('TODO: log provider and the state');
  }

  @override
  void providerDidFail(
    ProviderBase<dynamic> provider,
    Object error,
    StackTrace stackTrace,
    ProviderContainer container,
  ) {
    FirebaseCrashlytics.instance.recordError(error, stackTrace, fatal: true, reason: provider.name);

    log('TODO: log provider and the error/stackTrace');
  }
}

imo, the default behavior should handle that and have a single source of truth.

Furthermore, the current behavior of providerDidFail receive the error object instead of AsyncError (if thrown from an async provider), which is fine, but the previousValue could be helpful to track some issues.

As discussed at #3145 receiving an AsyncError might not be a good idea as we'll have duplicated StackTrace, but providerDidFail can alternatively receive a nullable previousValue, which will also be helpful if the error is thrown from a sync provider (after the first build).

The final result would be:

class ProviderLogger extends ProviderObserver {

  // This will not trigger if an exception is thrown on creation.
  @override
  void didAddProvider(
    ProviderBase<Object?> provider,
    Object? value,
    ProviderContainer container,
  ) {
    log('TODO: log provider and the state');
  }

  // This will not trigger if the newValue is an exception/AsyncError
  @override
  void didUpdateProvider(
    ProviderBase<dynamic> provider,
    Object? previousValue,
    Object? newValue,
    ProviderContainer container,
  ) {
    log('TODO: log provider and the state');
  }

  @override
  void providerDidFail(
    ProviderBase<dynamic> provider,
    Object? previousValue,
    Object error,
    StackTrace stackTrace,
    ProviderContainer container,
  ) {
    FirebaseCrashlytics.instance.recordError(
      error,
      stackTrace,
      fatal: true,
      reason: provider.name,
      information: [previousValue], //Reporting previous value can be helpful
    );

    log('TODO: log provider and the error/stackTrace');
  }
}

@lucavenir
Copy link
Sponsor Contributor

imo I'd love a breaking change better than having ambiguities or surprises.

Maybe plan this for v3?

@rrousselGit
Copy link
Owner

There's no ambiguity here. The behavior is intended. There's more than just logging as a use case for observers.
In particular, ProviderObserver was built in mind with the ability to allow writing a devtool.

Maybe it should be updated to make logging specifically simpler. But IMO that's not a huge concern.
It's not like people write 50 loggers in their applications. There's a limit to the value here

@rrousselGit rrousselGit added this to the Riverpod 3.0 milestone Mar 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants