riverpod icon indicating copy to clipboard operation
riverpod copied to clipboard

whenData() should preserve the value of AsyncLoading()

Open knaeckeKami opened this issue 7 months ago • 11 comments

Is your feature request related to a problem? Please describe.

Consider this gist: https://dartpad.dev/?id=ba69a9c1066bed38ea72ec8dd185f44d

This example consists of:

  • a FutureProvider userProvider that loads user data
  • an intermediary provider firstNameProvider that uses .selectAsync to extract the first name
  • a final provider greetingProvider that watches the intermediary provider and returns a greeting for the user

When the userProvider is invalidated, the UserProvider becomes an AsyncData(<old value>, isLoading:true) and finally switches to AsyncData(<new value>). The firstNameProvider however becomes a AsyncLoading<String>(value: ) for one frame when the userProvider updates, since .selectAsync returns a future which is only resolved on the next tick of the event loop. And the greeting provider, which uses .whenData() on firstNameProvider, will be AsyncLoading() without any value.

See this print of the values of these providers in the given sample:

AsyncData<({String firstName, String id, String lastName})>(value: (firstName: Abigail 902, id: 902, lastName: Miller)) 
AsyncLoading<String>(value: Abigail 538)
AsyncLoading<String>()

The first line is the userProvider with the new, updated value. The second line is the firstnameProvider which still has the old name as value, because the future returned by .selectAsync still has to resolve, indicated by the AsyncLoading type. The third line however does not have any value at all and is just an AsyncLoadin(), because it calls .whenData on an AsyncLoading() which does not preserve the value.

This causes a one-frame loading state on the widget that reads the greeting provider.

Describe the solution you'd like

I would propose a change to .whenData((data) => mapper(data)) to transform an AsyncLoading(value) to an AsyncLoading(mapper(value)) instead of an AsyncLoading() without value.

knaeckeKami avatar Nov 26 '23 21:11 knaeckeKami