docs: queryclient and query cache

This commit is contained in:
Kingkor Roy Tirtho
2023-10-06 23:04:22 +06:00
parent cf6a99504e
commit 3069b5696b
3 changed files with 81 additions and 114 deletions
-26
View File
@@ -1,26 +0,0 @@
---
title: Mutation Job
sidebar_position: 5
---
`MutationJob` is just like [`QueryJob`](/docs/basics/QueryJob) but for `Mutations`.
It is used to define how & where the new data is inserted or existing data is updated or deleted. Basically, it's the kind of Job that you'll use with http `POST/PUT/DELETE` requests but it doesn't have to be just HTTP requests, it can be anything that returns as long as it returns Future
Here's a simple example:
```dart
final basicMutationJob = MutationJob<Map, Map<String, dynamic>>(
mutationKey: "basic-mutation-example",
task: (key, data) async {
final response = await http.post(
Uri.parse("https://jsonplaceholder.typicode.com/posts"),
headers: {'Content-type': 'application/json; charset=UTF-8'},
body: jsonEncode(data),
);
return jsonDecode(response.body);
},
);
```
Here, instead of a `queryKey` there's a `mutationKey` parameter that is used to identify the Job.
`Mutation` also supports _retries_ but instead of `externalData` `Mutations` has `variables` parameter that used when the `mutate` method of Mutation is called where you can pass outside data to the `Mutation.task`
+81 -1
View File
@@ -2,6 +2,8 @@
sidebar_position: 1
---
### QueryClientProvider widget
The first thing needed for storing any form of data is a store. QueryClientProvider is basically a `InheritedWidget` which wraps around the actual store `QueryClient`. You must use wrap your `MaterialApp` or `CupertinoApp` with `QueryClientProvider` for using same `QueryClient` across all screens/routes. Or, if you want you can use `QueryClientProvider` anywhere in the Widget Tree to a different `QueryClient` to the descendant widgets
```dart
@@ -43,4 +45,82 @@ class MyApp extends StatelessWidget {
> If you provide `QueryClient` to `QueryClientProvider` then assign all the parameters to `QueryClient` itself
For more information on how to use QueryClientProvider, please refer to the [QueryClientProvider](https://pub.dev/documentation/fl_query/latest/fl_query/QueryClientProvider-class.html) API Reference
For more information on how to use QueryClientProvider, please refer to the [QueryClientProvider](https://pub.dev/documentation/fl_query/latest/fl_query/QueryClientProvider-class.html) API Reference
## QueryClient widget
The `QueryClient` is the store that holds all the query/mutation data. You can create a new instances of `QueryClient` by calling `QueryClient()` constructor. But it is recommended to use `QueryClientProvider` to create a new instance of `QueryClient` and use it across the app.
`QueryClient` can be accessed from anywhere in the widget tree using `QueryClient.of(context)`. It can be useful for imperative data manipulation
Often there are cases where imperative access to the API is necessary. e.g. invalidating/refreshing a query from another page after a mutation
`QueryClient` gives access to the base of this framework. SO BE CAREFUL WHILE USING IT
```dart
final queryClient = QueryClient.of(context);
```
You can create/pre-fetch a query like this:
```dart
await queryClient.fetchQuery(
'todos',
() => api.getTodos(),
);
```
> `queryClient.fetchQuery` will create and fetch a query immediately if it's not already available.
> To just create a query and not fetch it, use `queryClient.createQuery` instead
Also, you can refresh queries or queries that start with a certain prefix:
```dart
// Refresh a single query
await queryClient.refreshQuery(
'todos',
exact: true // pass false if you want to refresh a query with prefix
);
// Refresh multiple queries passing multiple keys
await queryClient.refreshQueries(['todos', 'posts']);
// Refresh queries with prefix
await queryClient.refreshQueriesWithPrefix('todo/');
```
:::tip
You can also use `QueryClient` to create, get, refresh/mutate `InfiniteQuery`(s) & `Mutation`(s)
:::
## QueryCache
Uhm, actually we kinda lied. QueryClient technically holds all the data but truly `QueryCache` is the one
that truly holds all the query and mutations. `QueryClient` is just a wrapper around `QueryCache` that provides some
useful methods and properties. `QueryCache` can be accessed using `QueryClient`'s cache property but it doesn't have any
useful methods or properties. So, it is recommended to use `QueryClient` instead of directly accessing the `QueryCache`
Also while using `QueryClient` you don't need to worry about `QueryCache` at all. It is just for the sake of knowledge
### Deleting queries/mutations from cache
One thing noticeable is there's no way to delete query/mutation using `QueryClient`. You have to use `QueryCache` for
that part. It is the only reason ever to use QueryCache
:::warning
Accessing the `QueryCache` directly is not recommended. And altering the cache can lead to unexpected behavior and
potential crashes
:::
Here's how to delete a query/mutation from cache:
```dart
final queryClient = QueryClient.of(context);
final query = queryClient.getQuery('todos');
final mutation = queryClient.getMutation('add-todos');
queryClient.cache.removeQuery(query);
queryClient.cache.removeMutation(mutation);
```
:::warning
Deleting a query/mutation from cache can be dangerous as it can cause memory leaks, infinite re-renders and crash if the
`Query`, `InfiniteQuery` or `Mutation` is still in use and mounted
:::
-87
View File
@@ -1,87 +0,0 @@
---
title: Query Job
sidebar_position: 2
---
Query Jobs are what you use to define the logic how or from where the data is fetched/queried. It is where the `task` function is defined. `QueryJob` is reusable throughout application
Here's a simple example
```dart
final job = QueryJob<String, void>(
queryKey: "a-unique-key",
task: (queryKey, externalData){
return Future.delayed(Duration(seconds: 1), () => "Hello World");
}
);
```
The `queryKey` must be unique. It is used to identify the job
The `task` callback has to be asynchronous. When the `task` is run by `Query` the `queryKey` & the `externalData` passed from `QueryBuilder` is passed to it as parameters. The externalData can be anything. You can provide a Generic Type parameter for it too
:::info
If `externalData` is of an `Iterable` type (`Map`, `List`, `Set` etc), it will be compared [shallowly](https://medium.com/nerdjacking/shallow-deep-comparison-9fd74ac0f3d2)
:::
### External Data
A more real-world example of `QueryJob` with `externalData`
```dart
import 'package:fl_query/fl_query.dart';
import 'package:http/http.dart';
final anotherJob = QueryJob<String, Client>(
queryKey: "another-unique-key",
task: (queryKey, httpClient){
return httpClient.get("https://jsonplaceholder.typicode.com/todos/1").then((response) => response.body);;
}
);
```
Here `externalData` is a configured `Client` from the `http` package.
By default when `externalData` changes or updates the query is not refetched but if you want it to refetch when the `externalData` changes, you can set `refetchOnExternalDataChange` property of `QueryJob` to `true`. If you want this behavior globally to be enabled then you can set `refetchOnExternalDataChange` property of [QueryBowlScope](/docs/basics/QueryBowlScope) to `true`
```dart
import 'package:fl_query/fl_query.dart';
import 'package:http/http.dart';
final anotherJob = QueryJob<String, Client>(
queryKey: "another-unique-key",
refetchOnExternalDataChange: true,
task: (queryKey, httpClient){
return httpClient.get("https://jsonplaceholder.typicode.com/todos/1").then((response) => response.body);;
}
);
```
Now every time when the externalData changes the query will refetched.
### Retries
When a query returns an `Exception` or in other word, fails, the query is re-run multiple times in the background until it succeeds or the retry limit is reached. You can configure the retry behavior of query by modifying `retries` & `retryDelay` properties of `QueryJob`
- `retries`: is amount of times the query will be retried before setting the status as `QueryStatus.error`. If its zero, it will not retry.
- `retryDelay`: is the `Duration` between retries. That means after what amount of duration the retries will take place until it succeeds or the retry limit is reached.
By default `retries` is `3` and `retryDelay` is `Duration(milliseconds: 200)`
```dart
final job = QueryJob<String, Client>(
queryKey: "exceptional-query",
retries: 10,
retryDelay: Duration(milliseconds: 200),
task: (queryKey, _) async {
throw Exception("I'm an evil Exception");
}
);
```
Now the query will be retried 10 times with a delay of 200ms between each retry
There are more properties of `QueryJob` that you can configure. See the API reference of [QueryJob](https://pub.dev/documentation/fl_query/latest/fl_query/QueryJob-class.html)