feat: pass variableKey to task fn in withVariableKey method

This commit is contained in:
Kingkor Roy Tirtho
2023-10-20 10:54:06 +06:00
parent 81a1fc1515
commit 53d5d7bc5e
8 changed files with 222 additions and 18 deletions
+186
View File
@@ -0,0 +1,186 @@
---
title: Jobs API
sidebar_position: 1
---
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
All this time, we've been directly supplying `queryKey`, `queryFn` etc to `QueryBuilder` and so on. But this might looks repetetive or might break the rules of DRY (Don't Repeat Yourself). So to tackle this issue Fl-Query has an alternate API to create queries/infinite queries/mutations.
Jobs are nothing but a collection of arguments of `*Builder`s. It is just a more declarative way to create queries/infinite queries/mutations.
## Query Job
A `QueryJob` consists of same arguments as `QueryBuilder`
```dart
final job = QueryJob<List<Todo>, dynamic, HttpApi>(
queryKey: "todos",
task: (api) => api.getTodos(),
);
```
You might notice the third type argument `HttpApi`. That's the new `ArgsType`. Because QueryJob is isolated from widget tree and outside APIs are not accessible from there, you've to manually pass anything you need to the `task` function. In this case, we need an instance of `HttpApi` to call `getTodos()`.
### `QueryBuilder.withJob` and `useQueryJob`
`QueryBuilder.withJob` is a static method that takes a `QueryJob` and returns a `QueryBuilder`. It also accepts event callbacks such as `onError` and `onData`. Also a special argument called `args` which is passed to the `task` function.
Similarly, we have a `useQueryJob` if you're using `flutter_hooks`
<Tabs>
<TabItem value="vanilla" label="Vanilla">
```dart
QueryBuilder.withJob(
job: job, // QueryJob we created earlier
args: httpApi,
onError: (error) => print(error),
onData: (data) => print(data),
builder: /* ... */,
);
```
</TabItem>
<TabItem value="flutter_hooks" label="Flutter Hooks">
```dart
useQueryJob(
job: job, // QueryJob we created earlier
args: httpApi,
onError: (error) => print(error),
onData: (data) => print(data),
builder: /* ... */,
);
```
</TabItem>
</Tabs>
## InfiniteQuery Job
Just like `QueryJob`, `InfiniteQueryJob` is the collection of arguments that is passed to `InfiniteQueryBuilder` or `useInfiniteQuery`
```dart
final job = InfiniteQueryBuilder<PagedProducts, ClientException, int, HttpApi>(
queryKey: "products",
task: (page, api) => api.getProductsPaginated(page),
nextPage: (lastPage, lastPageData) {
/// returning [null] will set [hasNextPage] to [false]
if (lastPageData.products.length < 10) return null;
return lastPage + 1;
},
initialPage: 0,
);
```
### `InfiniteQueryBuilder.withJob` and `useInfiniteQueryJob`
Just like `QueryBuilder.withJob`, `InfiniteQueryBuilder.withJob` is a static method that takes a `InfiniteQueryJob` and returns a `InfiniteQueryBuilder`. It also accepts event callbacks such as `onError` and `onData`. Also a special argument called `args` which is passed to the `task` function.
Simiarly, we have a `useInfiniteQueryJob` if you're using `flutter_hooks`
<Tabs>
<TabItem value="vanilla" label="Vanilla">
```dart
InfiniteQueryBuilder.withJob(
job: job, // InfiniteQueryJob we created earlier
args: httpApi,
onError: (error) => print(error),
onData: (data) => print(data),
builder: /* ... */,
);
```
</TabItem>
<TabItem value="flutter_hooks" label="Flutter Hooks">
```dart
useInfiniteQueryJob(
job: job, // InfiniteQueryJob we created earlier
args: httpApi,
onError: (error) => print(error),
onData: (data) => print(data),
builder: /* ... */,
);
```
</TabItem>
</Tabs>
## Mutation Job
For mutations we've `MutationJob` it is similar to `QueryJob` but has `mutationKey` instead of `queryKey`
```dart
final job = MutationJob<Todo, dynamic, Todo, void, HttpApi>(
mutationKey: "create-todo",
task: (variables, api) => api.createTodo(),
);
```
:::info
`MutationJob` has both `variables` and `args` which can be confusing. But use variables for passing data that are supposed to be sent, that means data that can change on next mutation.
Use `args` for passing anything that is not supposed to change, like an API instance, authentication info or a config etc.
:::
### `MutationBuilder.withJob` and `useMutationJob`
Just like `QueryBuilder.withJob`, `MutationBuilder.withJob` is a static method that takes a `MutationJob` and returns a `MutationBuilder`. It also accepts event callbacks such as `onMutate`, `onError` and `onData`. Also a special argument called `args` which is passed to the `task` function.
Simiarly, we have a `useMutationJob` if you're using `flutter_hooks`
<Tabs>
<TabItem value="vanilla" label="Vanilla">
```dart
MutationBuilder.withJob(
job: job, // MutationJob we created earlier
args: httpApi,
onError: (error) => print(error),
onData: (data) => print(data),
builder: /* ... */,
);
```
</TabItem>
<TabItem value="flutter_hooks" label="Flutter Hooks">
```dart
useInfiniteQueryJob(
job: job, // MutationJob we created earlier
args: httpApi,
onError: (error) => print(error),
onData: (data) => print(data),
builder: /* ... */,
);
```
</TabItem>
</Tabs>
## Dynamic Jobs
Every job has `.withVariableKey` static that allows creating queries/mutations with changing data on the fly. It returns a function that accepts a variable key (String). Which will create a new instance of the job with the new variable key.
Here's the example usage for `Query`:
```dart
final job = QueryJob.withVariableKey<List<Todo>, dynamic, HttpApi>(
baseQueryKey: "todos/", // the variable key will be appended to this
task: (variableKey, api) => api.getTodo(variableKey),
);
// later in the widget tree
QueryBuilder.withJob(
job: job(todoId),
args: httpApi,
builder: /* ... */,
);
```
Everything is just as same as for `Mutation` and `InfiniteQuery`..
+1 -1
View File
@@ -1,6 +1,6 @@
---
title: Optimistic Updates
sidebar_position: 1
sidebar_position: 2
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
+1 -1
View File
@@ -1,6 +1,6 @@
---
title: Persisting Queries
sidebar_position: 1
sidebar_position: 3
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
+3 -3
View File
@@ -8,9 +8,6 @@ Fl-Query V1 comes with lot of breaking changes in the API. It is not possible to
## Removal of APIs/methods/properties
- `QueryJob`, `MutationJob` and `InfiniteQueryJob` all were removed to bring the API closer to React Query. The `externalData` param was found to be vulnerable and was causing memory leaks. Also the query itself should not react to external data changes automatically which caused massive performance drawbacks and circular dependency related issues.
Now everything needs to be passed to `QueryBuilder`, `MutationBuilder` and `InfiniteQueryBuilder` or `useQuery`, `useMutation` and `useInfiniteQuery` hooks directly.
- `keepPreviousData` has been removed from both `Query` and `InfiniteQuery`
- `QueryClient`'s (formerly `QueryBowl`)
- `prefetchQuery` method was removed
@@ -51,6 +48,7 @@ Fl-Query V1 comes with lot of breaking changes in the API. It is not possible to
- `QueryBowlProvider` was renamed to `QueryClientProvider`
- `Query.refetch` was renamed to `Query.refresh`
- `InfiniteQuery.refetchPages` has been replaced with `InfiniteQuery.refresh` and `InfiniteQuery.refreshAll`
- `externalData` was renamed to `args` for `QueryJob`, `InfiniteQueryJob` and `MutationJob`
Now instead of passing callback, `Future.wait` is used with `refresh` to refetch a segment of pages.
`refreshAll` is a shorthand to refetch all pages at once.
@@ -62,6 +60,8 @@ Fl-Query V1 comes with lot of breaking changes in the API. It is not possible to
All the retry & cache invalidation related properties were moved to their own classes.
- `QueryBuilder`, `InfiniteQueryBuilder` and `MutationBuilder` doesn't accept jobs by default. To use the old Jobs API, `.withJob` constructor must be used.
- Similarly, `useQuery`, `useInfiniteQuery`, `useMutation` doesn't accept jobs now. Use `useQueryJob`, `useInfiniteQueryJob` and `useMutationJob` instead.
- Retry related properties were moved to `RetryConfig` class
- `retries` was renamed to `maxRetries`
- Cache invalidation related properties were moved to `RefreshConfig` class
@@ -6,6 +6,13 @@ import 'package:fl_query/src/core/infinite_query.dart';
typedef InfiniteQueryJobFn<DataType, PageType, ArgsType> = Future<DataType>
Function(PageType page, ArgsType args);
typedef InfiniteQueryJobVariableFn<DataType, PageType, ArgsType>
= Future<DataType> Function(
String variableKey,
PageType page,
ArgsType args,
);
typedef InfiniteQueryJobVariableKeyFn<DataType, ErrorType, PageType, ArgsType>
= InfiniteQueryJob<DataType, ErrorType, PageType, ArgsType> Function(
String variable);
@@ -40,7 +47,7 @@ class InfiniteQueryJob<DataType, ErrorType, PageType, ArgsType> {
static InfiniteQueryJobVariableKeyFn<DataType, ErrorType, PageType, ArgsType>
withVariableKey<DataType, ErrorType, PageType, ArgsType>({
required String baseQueryKey,
required InfiniteQueryJobFn<DataType, PageType, ArgsType?> task,
required InfiniteQueryJobVariableFn<DataType, PageType, ArgsType?> task,
required final InfiniteQueryNextPage<DataType, PageType> nextPage,
required final PageType initialPage,
RetryConfig? retryConfig,
@@ -48,9 +55,9 @@ class InfiniteQueryJob<DataType, ErrorType, PageType, ArgsType> {
JsonConfig<DataType>? jsonConfig,
bool enabled = true,
}) {
return (String variable) => InfiniteQueryJob(
queryKey: "$baseQueryKey$variable",
task: task,
return (String variableKey) => InfiniteQueryJob(
queryKey: "$baseQueryKey$variableKey",
task: (page, args) => task(variableKey, page, args),
nextPage: nextPage,
initialPage: initialPage,
retryConfig: retryConfig,
@@ -6,6 +6,13 @@ typedef MutationJobFn<DataType, VariablesType, ArgsType> = Future<DataType>
ArgsType args,
);
typedef MutationJobVariableFn<DataType, VariablesType, ArgsType>
= Future<DataType> Function(
String variableKey,
VariablesType variables,
ArgsType args,
);
typedef MutationJobVariableKeyFn<DataType, ErrorType, VariablesType,
RecoveryType, ArgsType>
= MutationJob<DataType, ErrorType, VariablesType, RecoveryType, ArgsType>
@@ -33,14 +40,14 @@ class MutationJob<DataType, ErrorType, VariablesType, RecoveryType, ArgsType> {
withVariableKey<DataType, ErrorType, VariablesType, RecoveryType,
ArgsType>({
required String baseMutationKey,
required MutationJobFn<DataType, VariablesType, ArgsType?> task,
required MutationJobVariableFn<DataType, VariablesType, ArgsType?> task,
RetryConfig? retryConfig,
List<String>? refreshQueries,
List<String>? refreshInfiniteQueries,
}) {
return (String variable) => MutationJob(
mutationKey: "$baseMutationKey$variable",
task: task,
return (String variableKey) => MutationJob(
mutationKey: "$baseMutationKey$variableKey",
task: (variables, args) => task(variableKey, variables, args),
retryConfig: retryConfig,
refreshQueries: refreshQueries,
refreshInfiniteQueries: refreshInfiniteQueries,
@@ -4,6 +4,10 @@ import 'package:fl_query/src/collections/retry_config.dart';
typedef QueryJobFn<DataType, ArgsType> = Future<DataType> Function(
ArgsType args);
typedef QueryJobVariableFn<DataType, ArgsType> = Future<DataType> Function(
String variableKey,
ArgsType args,
);
typedef QueryJobVariableKeyFn<DataType, ErrorType, ArgsType>
= QueryJob<DataType, ErrorType, ArgsType> Function(String variable);
@@ -37,16 +41,16 @@ class QueryJob<DataType, ErrorType, ArgsType> {
static QueryJobVariableKeyFn<DataType, ErrorType, ArgsType>
withVariableKey<DataType, ErrorType, ArgsType>({
required String baseQueryKey,
required QueryJobFn<DataType, ArgsType?> task,
required QueryJobVariableFn<DataType, ArgsType?> task,
DataType? initial,
RetryConfig? retryConfig,
RefreshConfig? refreshConfig,
JsonConfig<DataType>? jsonConfig,
bool enabled = true,
}) {
return (String variable) => QueryJob(
queryKey: "$baseQueryKey$variable",
task: task,
return (String variableKey) => QueryJob(
queryKey: "$baseQueryKey$variableKey",
task: (args) => task(variableKey, args),
initial: initial,
retryConfig: retryConfig,
refreshConfig: refreshConfig,
@@ -4,9 +4,9 @@ import 'package:flutter/material.dart';
Query<DataType, ErrorType> useQueryJob<DataType, ErrorType, ArgsType>({
required QueryJob<DataType, ErrorType, ArgsType> job,
required ArgsType args,
ValueChanged<DataType>? onData,
ValueChanged<ErrorType>? onError,
required ArgsType args,
}) {
return useQuery<DataType, ErrorType>(
job.queryKey,