docs: add advanced category, move optimistic updates & persisting queries to advanced
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Optimistic Updates
|
title: Optimistic Updates
|
||||||
sidebar_position: 10
|
sidebar_position: 1
|
||||||
---
|
---
|
||||||
import Tabs from '@theme/Tabs';
|
import Tabs from '@theme/Tabs';
|
||||||
import TabItem from '@theme/TabItem';
|
import TabItem from '@theme/TabItem';
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
---
|
||||||
|
title: Persisting Queries
|
||||||
|
sidebar_position: 1
|
||||||
|
---
|
||||||
|
import Tabs from '@theme/Tabs';
|
||||||
|
import TabItem from '@theme/TabItem';
|
||||||
|
|
||||||
|
Implementing offline capabilites by your own can be hard to do (and maintain!!) so Fl-Query makes easier for developers to do it. Fl-Query uses [Hive](https://docs.hivedb.dev/#/) to persist queries to disk. Hive is a Open Source, "lightweight and buzzing-fast key-value database made for Flutter and Dart."
|
||||||
|
|
||||||
|
We're using hive because it supports storing unstructured data and the write speed is blazingly fast
|
||||||
|
|
||||||
|
But it is planned to support multiple types of Database for persisting using an Adapter pattern
|
||||||
|
|
||||||
|
### Persisting Queries
|
||||||
|
|
||||||
|
Queries can be persisted by passing `jsonConfig` argument to the `QueryBuilder` or `useQuery`. Persisted queries are stored in [hive](https://docs.hivedb.dev/) cache and are available even after the app is restarted
|
||||||
|
|
||||||
|
First make sure your custom data type is json serializable. You can use [json_serializable](https://pub.dev/packages/json_serializable) package to generate `toJson` and `fromJson` methods for your data type
|
||||||
|
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'todo.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class Todo{
|
||||||
|
final String id;
|
||||||
|
final String title;
|
||||||
|
final bool completed;
|
||||||
|
|
||||||
|
Todo({
|
||||||
|
required this.id,
|
||||||
|
required this.title,
|
||||||
|
required this.completed,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Todo.fromJson(Map<String, dynamic> json) => _$TodoFromJson(json);
|
||||||
|
Map<String, dynamic> toJson() => _$TodoToJson(this);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<TabItem value="vanilla" label="Vanilla">
|
||||||
|
|
||||||
|
```dart
|
||||||
|
QueryBuilder<Todo, HttpException>(
|
||||||
|
"todos",
|
||||||
|
() => api.getTodos(),
|
||||||
|
jsonConfig: JsonConfig(
|
||||||
|
fromJson: (json) => Todo.fromJson(json),
|
||||||
|
toJson: (todo) => todo.toJson(),
|
||||||
|
),
|
||||||
|
builder: (context, query) {
|
||||||
|
/* ... */
|
||||||
|
},
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="flutter_hooks" label="Flutter Hooks">
|
||||||
|
|
||||||
|
```dart
|
||||||
|
useQuery<Todo, HttpException>(
|
||||||
|
"todos",
|
||||||
|
() => api.getTodos(),
|
||||||
|
jsonConfig: JsonConfig(
|
||||||
|
fromJson: (json) => Todo.fromJson(json),
|
||||||
|
toJson: (todo) => todo.toJson(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
Right now due to lack of reflection support and compile time macros we're unable to serialize any data type on the fly.
|
||||||
|
That's why `JsonConfig` is required. Otherwise, a simple `persistToDisk: true` would have been enough
|
||||||
|
|
||||||
|
### Persisting InfiniteQueries
|
||||||
|
|
||||||
|
Just like `Query`, `InfiniteQuery` also accepts `jsonConfig` argument to persist page data to disk. With persisting `InfiniteQuery` you can achieve similar results like facebook/twitter's offline mode
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<TabItem value="vanilla" label="Vanilla">
|
||||||
|
|
||||||
|
```dart
|
||||||
|
InfiniteQueryBuilder<PagedPosts, ClientException, int>(
|
||||||
|
"posts",
|
||||||
|
(page) => api.getPostsPaginated(page),
|
||||||
|
nextPage: (lastPage, lastPageData) {
|
||||||
|
/// returning [null] will set [hasNextPage] to [false]
|
||||||
|
if (lastPageData.posts.length < 10) return null;
|
||||||
|
return lastPage + 1;
|
||||||
|
},
|
||||||
|
initialPage: 0,
|
||||||
|
jsonConfig: JsonConfig(
|
||||||
|
fromJson: (json)=> PagedPosts.fromJson(json),
|
||||||
|
toJson: (pagedPosts) => pagedPosts.toJson(),
|
||||||
|
),
|
||||||
|
builder: /*...*/
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
<TabItem value="flutter_hooks" label="Flutter Hooks">
|
||||||
|
|
||||||
|
```dart
|
||||||
|
final query = useInfiniteQuery<PagedPosts, ClientException, int>(
|
||||||
|
"posts",
|
||||||
|
(page) => api.getPostsPaginated(page),
|
||||||
|
nextPage: (lastPage, lastPageData) {
|
||||||
|
/// returning [null] will set [hasNextPage] to [false]
|
||||||
|
if (lastPageData.posts.length < 10) return null;
|
||||||
|
return lastPage + 1;
|
||||||
|
},
|
||||||
|
initialPage: 0,
|
||||||
|
jsonConfig: JsonConfig(
|
||||||
|
fromJson: (json)=> PagedPosts.fromJson(json),
|
||||||
|
toJson: (pagedPosts) => pagedPosts.toJson(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
</TabItem>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
This will persist each available page to a HiveStore
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"label": "Advanced",
|
||||||
|
"position": 3,
|
||||||
|
"link": {
|
||||||
|
"type": "generated-index",
|
||||||
|
"description": "Advanced caching and data manipulation techniques to make your app better"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -217,7 +217,7 @@ await Future.wait(
|
|||||||
|
|
||||||
### Set page data manually
|
### Set page data manually
|
||||||
|
|
||||||
`InfiniteQuery` provides a method `InfiniteQuery.setPageData` to set page data manually. This can be useful if you want to set data after a mutation for [Optmisitc Updates](/docs/basics/OptimisticUpdates)
|
`InfiniteQuery` provides a method `InfiniteQuery.setPageData` to set page data manually. This can be useful if you want to set data after a mutation for [Optmisitc Updates](/docs/advanced/OptimisticUpdates)
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
query.setPageData(0, [...query.pages[0], newProduct])
|
query.setPageData(0, [...query.pages[0], newProduct])
|
||||||
@@ -309,56 +309,4 @@ final query = useInfiniteQuery<PagedProducts, ClientException, int>(
|
|||||||
```
|
```
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
### Persisting InfiniteQueries
|
|
||||||
|
|
||||||
The `pages` data can be saved to disk to be loaded next time when the app starts to reduce loading time of your app. Just like [`Query`](/docs/basics/Queries#persisting-queries) persisted `InfiniteQuery`'s `pages` data are stored in [hive](https://docs.hivedb.dev/#/). This can be widely useful to add offline capabilities to the app
|
|
||||||
|
|
||||||
For this you need to pass a `jsonConfig: JsonConfig()` argument to `InfiniteQueryBuilder` or `useInfiniteQuery`. Also make sure your `DataType` is serializable to JSON. You can use [json_serializable](https://pub.dev/packages/json_serializable) to generate the code for you.
|
|
||||||
|
|
||||||
<Tabs>
|
|
||||||
<TabItem value="vanilla" label="Vanilla">
|
|
||||||
|
|
||||||
```dart
|
|
||||||
InfiniteQueryBuilder<PagedPosts, ClientException, int>(
|
|
||||||
"posts",
|
|
||||||
(page) => api.getPostsPaginated(page),
|
|
||||||
nextPage: (lastPage, lastPageData) {
|
|
||||||
/// returning [null] will set [hasNextPage] to [false]
|
|
||||||
if (lastPageData.posts.length < 10) return null;
|
|
||||||
return lastPage + 1;
|
|
||||||
},
|
|
||||||
initialPage: 0,
|
|
||||||
jsonConfig: JsonConfig(
|
|
||||||
fromJson: (json)=> PagedPosts.fromJson(json),
|
|
||||||
toJson: (pagedPosts) => pagedPosts.toJson(),
|
|
||||||
),
|
|
||||||
builder: /*...*/
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
<TabItem value="flutter_hooks" label="Flutter Hooks">
|
|
||||||
|
|
||||||
```dart
|
|
||||||
final query = useInfiniteQuery<PagedPosts, ClientException, int>(
|
|
||||||
"posts",
|
|
||||||
(page) => api.getPostsPaginated(page),
|
|
||||||
nextPage: (lastPage, lastPageData) {
|
|
||||||
/// returning [null] will set [hasNextPage] to [false]
|
|
||||||
if (lastPageData.posts.length < 10) return null;
|
|
||||||
return lastPage + 1;
|
|
||||||
},
|
|
||||||
initialPage: 0,
|
|
||||||
jsonConfig: JsonConfig(
|
|
||||||
fromJson: (json)=> PagedPosts.fromJson(json),
|
|
||||||
toJson: (pagedPosts) => pagedPosts.toJson(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
This will persist each available page to a HiveStore
|
|
||||||
@@ -133,7 +133,7 @@ final mutation = useMutation<Map<String, dynamic>, dynamic, Map<String, dynamic>
|
|||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
> Learn how to use `onMutate` & `Query.setData` to implement [optimistic updates](/docs/basics/OptimisticUpdates)
|
> Learn how to use `onMutate` & `Query.setData` to implement [optimistic updates](/docs/advanced/OptimisticUpdates)
|
||||||
|
|
||||||
### Refetch Queries and InfiniteQueries on successful mutation
|
### Refetch Queries and InfiniteQueries on successful mutation
|
||||||
|
|
||||||
|
|||||||
@@ -190,70 +190,4 @@ useQuery<String, HttpException>(
|
|||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
Now these queries won't be executed as soon as they're mounted. Until `Query.refresh()` or `Query.fetch()` is called these will stay in initial state. If `initial` data was passed, it'll be used until the query is refreshed. Same goes for persisting queries
|
Now these queries won't be executed as soon as they're mounted. Until `Query.refresh()` or `Query.fetch()` is called these will stay in initial state. If `initial` data was passed, it'll be used until the query is refreshed. Same goes for persisting queries
|
||||||
|
|
||||||
### Persisting Queries
|
|
||||||
|
|
||||||
Queries can be persisted by passing `jsonConfig` argument to the `QueryBuilder` or `useQuery`. Persisted queries are stored in [hive](https://docs.hivedb.dev/) cache and are available even after the app is restarted
|
|
||||||
|
|
||||||
First make sure your custom data type is json serializable. You can use [json_serializable](https://pub.dev/packages/json_serializable) package to generate `toJson` and `fromJson` methods for your data type
|
|
||||||
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
|
|
||||||
part 'todo.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class Todo{
|
|
||||||
final String id;
|
|
||||||
final String title;
|
|
||||||
final bool completed;
|
|
||||||
|
|
||||||
Todo({
|
|
||||||
required this.id,
|
|
||||||
required this.title,
|
|
||||||
required this.completed,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory Todo.fromJson(Map<String, dynamic> json) => _$TodoFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$TodoToJson(this);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<Tabs>
|
|
||||||
<TabItem value="vanilla" label="Vanilla">
|
|
||||||
|
|
||||||
```dart
|
|
||||||
QueryBuilder<Todo, HttpException>(
|
|
||||||
"todos",
|
|
||||||
() => api.getTodos(),
|
|
||||||
jsonConfig: JsonConfig(
|
|
||||||
fromJson: (json) => Todo.fromJson(json),
|
|
||||||
toJson: (todo) => todo.toJson(),
|
|
||||||
),
|
|
||||||
builder: (context, query) {
|
|
||||||
/* ... */
|
|
||||||
},
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
<TabItem value="flutter_hooks" label="Flutter Hooks">
|
|
||||||
|
|
||||||
```dart
|
|
||||||
useQuery<Todo, HttpException>(
|
|
||||||
"todos",
|
|
||||||
() => api.getTodos(),
|
|
||||||
jsonConfig: JsonConfig(
|
|
||||||
fromJson: (json) => Todo.fromJson(json),
|
|
||||||
toJson: (todo) => todo.toJson(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
Right now due to lack of reflection support and compile time macros we're unable to serialize any data type on the fly.
|
|
||||||
That's why `JsonConfig` is required. Otherwise, a simple `persistToDisk: true` would have been enough
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"label": "Basics",
|
"label": "Basics",
|
||||||
"position": 3,
|
"position": 2,
|
||||||
"link": {
|
"link": {
|
||||||
"type": "generated-index",
|
"type": "generated-index",
|
||||||
"description": "Learn all the basic concepts of Fl-Query as well as the practical use-cases that can enhance your development experience"
|
"description": "Learn all the basic concepts of Fl-Query as well as the practical use-cases that can enhance your development experience"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"label": "Getting Started",
|
"label": "Getting Started",
|
||||||
"position": 1,
|
"position": 1,
|
||||||
"link": {
|
"link": {
|
||||||
"type": "generated-index"
|
"type": "generated-index"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
---
|
---
|
||||||
sidebar_position: 1
|
sidebar_position: 1
|
||||||
|
title: Overview
|
||||||
id: overview
|
id: overview
|
||||||
---
|
---
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Asynchronous data caching, refetching & invalidation library for Flutter. FL-Query lets you manage & distribute your async data without touching any global state
|
Asynchronous data caching, refetching & invalidation library for Flutter. FL-Query lets you manage & distribute your async data without touching any global state
|
||||||
|
|
||||||
Fl-Query makes asynchronous server state management a breeze in flutter
|
Fl-Query makes asynchronous server state management a breeze in flutter
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"label": "Tutorial - Extras",
|
|
||||||
"position": 4,
|
|
||||||
"link": {
|
|
||||||
"type": "generated-index"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB |
@@ -1,55 +0,0 @@
|
|||||||
---
|
|
||||||
sidebar_position: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
# Manage Docs Versions
|
|
||||||
|
|
||||||
Docusaurus can manage multiple versions of your docs.
|
|
||||||
|
|
||||||
## Create a docs version
|
|
||||||
|
|
||||||
Release a version 1.0 of your project:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run docusaurus docs:version 1.0
|
|
||||||
```
|
|
||||||
|
|
||||||
The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created.
|
|
||||||
|
|
||||||
Your docs now have 2 versions:
|
|
||||||
|
|
||||||
- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs
|
|
||||||
- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs**
|
|
||||||
|
|
||||||
## Add a Version Dropdown
|
|
||||||
|
|
||||||
To navigate seamlessly across versions, add a version dropdown.
|
|
||||||
|
|
||||||
Modify the `docusaurus.config.js` file:
|
|
||||||
|
|
||||||
```js title="docusaurus.config.js"
|
|
||||||
module.exports = {
|
|
||||||
themeConfig: {
|
|
||||||
navbar: {
|
|
||||||
items: [
|
|
||||||
// highlight-start
|
|
||||||
{
|
|
||||||
type: 'docsVersionDropdown',
|
|
||||||
},
|
|
||||||
// highlight-end
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
The docs version dropdown appears in your navbar:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Update an existing version
|
|
||||||
|
|
||||||
It is possible to edit versioned docs in their respective folder:
|
|
||||||
|
|
||||||
- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello`
|
|
||||||
- `docs/hello.md` updates `http://localhost:3000/docs/next/hello`
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
---
|
|
||||||
sidebar_position: 2
|
|
||||||
---
|
|
||||||
|
|
||||||
# Translate your site
|
|
||||||
|
|
||||||
Let's translate `docs/intro.md` to French.
|
|
||||||
|
|
||||||
## Configure i18n
|
|
||||||
|
|
||||||
Modify `docusaurus.config.js` to add support for the `fr` locale:
|
|
||||||
|
|
||||||
```js title="docusaurus.config.js"
|
|
||||||
module.exports = {
|
|
||||||
i18n: {
|
|
||||||
defaultLocale: 'en',
|
|
||||||
locales: ['en', 'fr'],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Translate a doc
|
|
||||||
|
|
||||||
Copy the `docs/intro.md` file to the `i18n/fr` folder:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/
|
|
||||||
|
|
||||||
cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md
|
|
||||||
```
|
|
||||||
|
|
||||||
Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French.
|
|
||||||
|
|
||||||
## Start your localized site
|
|
||||||
|
|
||||||
Start your site on the French locale:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run start -- --locale fr
|
|
||||||
```
|
|
||||||
|
|
||||||
Your localized site is accessible at `http://localhost:3000/fr/` and the `Getting Started` page is translated.
|
|
||||||
|
|
||||||
:::caution
|
|
||||||
|
|
||||||
In development, you can only use one locale at a same time.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Add a Locale Dropdown
|
|
||||||
|
|
||||||
To navigate seamlessly across languages, add a locale dropdown.
|
|
||||||
|
|
||||||
Modify the `docusaurus.config.js` file:
|
|
||||||
|
|
||||||
```js title="docusaurus.config.js"
|
|
||||||
module.exports = {
|
|
||||||
themeConfig: {
|
|
||||||
navbar: {
|
|
||||||
items: [
|
|
||||||
// highlight-start
|
|
||||||
{
|
|
||||||
type: 'localeDropdown',
|
|
||||||
},
|
|
||||||
// highlight-end
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
The locale dropdown now appears in your navbar:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Build your localized site
|
|
||||||
|
|
||||||
Build your site for a specific locale:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build -- --locale fr
|
|
||||||
```
|
|
||||||
|
|
||||||
Or build your site to include all the locales at once:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
Reference in New Issue
Block a user