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
|
||||
---
|
||||
|
||||
### 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
|
||||
:::
|
||||
|
||||
@@ -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