From 8eaa6b04d1f123517e28e768219af7490ddef57a Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sun, 7 Aug 2022 12:13:03 +0600 Subject: [PATCH] docs: add paginated-query section and update optimistc update section --- docs/docs/basics/OptimisticUpdates.md | 12 ++- docs/docs/basics/PaginatedQuery.md | 99 +++++++++++++++++++ docs/docs/getting-started/installation.md | 2 +- .../lib/components/mutation_variable_key.dart | 3 +- .../mutation_hook_variable_key.dart | 3 +- 5 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 docs/docs/basics/PaginatedQuery.md diff --git a/docs/docs/basics/OptimisticUpdates.md b/docs/docs/basics/OptimisticUpdates.md index 9455629..83bbcbe 100644 --- a/docs/docs/basics/OptimisticUpdates.md +++ b/docs/docs/basics/OptimisticUpdates.md @@ -1,5 +1,5 @@ --- -title: Optimistic Updates (Still WIP) +title: Optimistic Updates sidebar_position: 10 --- @@ -34,6 +34,7 @@ return MutationBuilder( return MutationBuilder( job: mutationJob, onMutate: (variable) { + final data = QueryBowl.of(context).getQuery(successJob.queryKey)?.data; QueryBowl.of(context) .setQueryData, void>(successJob.queryKey, (oldData) { // replacing the soon to be expired data with updated data @@ -44,6 +45,15 @@ return MutationBuilder( // of the intended query data which can be used when // an error occurs in mutation & we can rollback to a previous // data set + return data; + }, + onData: (data, variables, context) { + print("Passed Variable: $variables"); + print("Safe Previous Value: $context"); + }, + onError: (data, variables, context) { + print("Passed Variable: $variables"); + print("Safe Previous Value: $context"); } ); ``` \ No newline at end of file diff --git a/docs/docs/basics/PaginatedQuery.md b/docs/docs/basics/PaginatedQuery.md new file mode 100644 index 0000000..5b539ef --- /dev/null +++ b/docs/docs/basics/PaginatedQuery.md @@ -0,0 +1,99 @@ +--- +title: Paginated/Lagged Query +sidebar_position: 10 +--- + + +Rendering paginated data is a very common UI pattern and in Fl-Query, it "just works" by including the page information in the query key: + +```dart +final queryVariableKeyJob = QueryJob.withVariableKey( + task: (queryKey, externalData) { + return MyAPI.getData(id: getVariable(queryKey)); + }, +); + +/// inside a widget build method +QueryBuilder( + job: queryVariableKeyJob(id), + externalData: null, + builder: (context, query){...} +) +``` + +However, if you run this simple example, you might notice something strange: + +**The UI jumps in and out of the `success` and `loading` states because each new page is treated like a brand new query.** + +This experience is not optimal and unfortunately is how many tools today insist on working. But not Fl-Query! As you may have guessed, Fl-Query comes with an awesome feature called `keepPreviousData` that allows us to get around this. + +## Better Paginated Queries with `keepPreviousData` + +Consider the following example where we would ideally want to increment a pageIndex (or cursor) for a query. If we were to use just `QueryJob.withVariableKey`, **it would still technically work fine**, but the UI would jump in and out of the `success` and `loading` states as different queries are created and destroyed for each page or cursor. By setting `keepPreviousData` to `true` we get a few new things: + +- **The data from the last successful fetch available while new data is being requested, even though the query key has changed**. +- When the new data arrives, the previous `data` is seamlessly swapped to show the new data. +- `isPreviousData` is made available to know what data the query is currently providing you + +```dart +final todoJob = QueryJob.withVariableKey( + preQueryKey: "todo", + task: (queryKey, _) async { + final res = await http.get( + Uri.parse( + "https://jsonplaceholder.typicode.com/todos/${getVariable(queryKey)}"), + ); + return jsonDecode(res.body); + }, + keepPreviousData: true, +); + +class QueryPreviousDataExample extends StatefulWidget { + const QueryPreviousDataExample({Key? key}) : super(key: key); + + @override + State createState() => + _QueryPreviousDataExampleState(); +} + +class _QueryPreviousDataExampleState extends State { + int id = 1; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + QueryBuilder( + job: todoJob(id.toString()), + externalData: null, + builder: (context, query) { + if (query.hasError) return Text(query.error.toString()); + if (!query.hasData) return const CircularProgressIndicator(); + return Text(jsonEncode(query.data ?? {})); + }), + Row( + children: [ + IconButton( + icon: const Icon(Icons.remove), + onPressed: () { + setState(() { + id -= 1; + }); + }, + ), + IconButton( + icon: const Icon(Icons.add), + onPressed: () { + setState(() { + id += 1; + }); + }, + ), + ], + ) + ], + ); + } +} +``` \ No newline at end of file diff --git a/docs/docs/getting-started/installation.md b/docs/docs/getting-started/installation.md index ca30f4b..d6952a4 100644 --- a/docs/docs/getting-started/installation.md +++ b/docs/docs/getting-started/installation.md @@ -9,7 +9,7 @@ Fl-Query is just another Flutter "package" so no extra installation step needed $ flutter pub add fl_query ``` -### For using with `flutter_hooks` +### Using with `flutter_hooks` If you're an ELITE `flutter_hooks` user or want to use `fl_query_hooks` you'll need the `flutter_hooks` & `fl_query_hooks` package diff --git a/packages/fl_query/example/lib/components/mutation_variable_key.dart b/packages/fl_query/example/lib/components/mutation_variable_key.dart index ff11fb5..ef69204 100644 --- a/packages/fl_query/example/lib/components/mutation_variable_key.dart +++ b/packages/fl_query/example/lib/components/mutation_variable_key.dart @@ -4,6 +4,7 @@ import 'package:fl_query/fl_query.dart'; import 'package:flutter/material.dart'; final mutationVariableKeyJob = MutationJob.withVariableKey( + preMutationKey: "mutation-example", task: (queryKey, variables) { return Future.value("$variables"); }, @@ -37,7 +38,7 @@ class _MutationVariableKeyExampleState style: Theme.of(context).textTheme.headline5, ), MutationBuilder( - job: mutationVariableKeyJob("mutation-variable-key#$id"), + job: mutationVariableKeyJob(id.toString()), builder: (context, mutation) { return Row( children: [ diff --git a/packages/fl_query_hooks/example/lib/components/mutation_hook_variable_key.dart b/packages/fl_query_hooks/example/lib/components/mutation_hook_variable_key.dart index bae91de..f3b1f70 100644 --- a/packages/fl_query_hooks/example/lib/components/mutation_hook_variable_key.dart +++ b/packages/fl_query_hooks/example/lib/components/mutation_hook_variable_key.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; final mutationHookVariableKeyJob = MutationJob.withVariableKey( + preMutationKey: "mutation-example", task: (queryKey, variables) { return Future.value("$variables"); }, @@ -17,7 +18,7 @@ class MutationHookVariableKeyExample extends HookWidget { Widget build(BuildContext context) { final id = useState(Random().nextDouble()); final mutation = useMutation( - job: mutationHookVariableKeyJob("mutation-hook-variable-key#${id.value}"), + job: mutationHookVariableKeyJob(id.value.toString()), ); return Column( crossAxisAlignment: CrossAxisAlignment.start,