docs: queryclient and query cache
This commit is contained in:
@@ -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`
|
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
sidebar_position: 1
|
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
|
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
|
```dart
|
||||||
@@ -44,3 +46,81 @@ class MyApp extends StatelessWidget {
|
|||||||
> If you provide `QueryClient` to `QueryClientProvider` then assign all the parameters to `QueryClient` itself
|
> 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
|
||||||
|
:::
|
||||||
|
|||||||
@@ -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)
|
|
||||||
Reference in New Issue
Block a user