Home > Back-end >  How to get previous value of a provider in Riverpod?
How to get previous value of a provider in Riverpod?

Time:10-18

The code below takes user input, and prints it in the upper case after a delay of 1s.

Minimal reproducible code:

class FooPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final asyncResult = ref.watch(resultProvider);
    return Scaffold(
      body: Column(
        children: [
          TextField(onChanged: (s) => ref.read(queryProvider.notifier).state = s),
          asyncResult.when(
            data: Text.new,
            error: (e, s) => Text('Error = $e'),
            loading: () => Text('Loading...'),
          ),
        ],
      ),
    );
  }
}

final stringProvider = FutureProvider.family<String, String>((ref, query) async {
  await Future.delayed(Duration(seconds: 1));
  return query.toUpperCase();
});

final queryProvider = StateProvider<String>((ref) => '');

final resultProvider = FutureProvider<String>((ref) async {
  final query = ref.watch(queryProvider);
  return ref.watch(stringProvider(query).future);
});

After running the code,

  1. Enter any text (say a) and wait for the output (the upper case)
  2. Enter another text (say b), and wait for the output (the upper case)
  3. Press the backspace (i.e. delete the character b) and you'll notice that the provider goes in the loading state for a fraction of seconds (which makes the loading widget to get called).

This issue is happening on the 2.0.2 version. So, how can I get the previous value so that I can consistently show data once a data is fetched?

CodePudding user response:

after upgrading my riverpod to 2.0.2, i now can see the behavior. The main reason for that is because of the 1 second delay. And there is no way you can eliminate that loading unless you remove the delay because everytime there is a change in your query, the future provider is being called, i tried it and its working fine. Now for your main question that how to access the previous state of a provider, you can do that using ref.listen(). here below it the demo on how you can access it.

class FooPage extends ConsumerWidget {
  const FooPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    
    ref.listen<AsyncValue<String>>(resultProvider, (previous, next) {
      //* you can access the previous state here
      if (previous?.value != '' && previous?.value != null) {
        ref.read(upperCaseQuery.notifier).state = previous?.value ?? '';
      }
    });
    final resultValue = ref.watch(resultProvider);
    final upperCased = ref.watch(upperCaseQuery.state).state;
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(onChanged: (s) {
              ref.read(queryProvider.notifier).state = s;
            }),
            //previous value of the the provider
            Text(upperCased),

            resultValue.when(
              data: (value) =>  Text(value),
              error: (e, s) => Text('Error = $e'),
              loading: () => const Text('Loading...'),
            ),
          ],
        ),
      ),
    );
  }
}


final queryProvider = StateProvider<String>((ref) => 'default');

final resultProvider = FutureProvider.autoDispose<String>((ref) async {
  final query = ref.watch(queryProvider);
  await Future.delayed(const Duration(seconds: 1));
  final upperCased = query.toUpperCase();
  return upperCased;
}, name: 'result provider');

final upperCaseQuery = StateProvider<String>((ref) => '', name: 'upper cased');

CodePudding user response:

If you move await Future.delayed(const Duration(seconds: 1)); from stringProvider to resultProvider everything will work as you would expect.

Also, let me offer you a variant of code modernization:

/// Instead `stringProvider`.
Future<String> stringConvert(String query) async {
  await Future.delayed(const Duration(seconds: 1));

  return query.toUpperCase();
}

final queryProvider = StateProvider<String>((ref) {
  return '';
});

final resultProvider = FutureProvider<String>((ref) async {
  final query = ref.watch(queryProvider);
  return stringConvert(query);
});
  • Related