Async Data
The core of TanStack Query is to manage server-side data. Below, we demonstrate how to implement CRUD functionality in Model
- For complete code examples, please see: demo-todo
Data Query: select
How to define
typescript
@Model()
export class ModelTodo {
select() {
return this.$useQueryExisting({
queryKey: ['select'],
queryFn: async () => {
return this.scope.service.todo.select();
},
});
}
}
- Invoke
$useQueryExisting
to create a Query object- Why not use the
$useQuery
method? Because asynchronous data is generally loaded asynchronously when needed. Therefore, we need to ensure that the same Query object is always returned when theselect
method is invoked multiple times, so the$useQueryExisting
method must be used
- Why not use the
- Pass in
queryKey
to ensure the uniqueness of the local cache - Pass in
queryFn
and call this function at the appropriate time to obtain server data- service.todo.select: see Api service
How to use
demo-todo/src/page/todo/controller.ts
typescript
import { ModelTodo } from '../../bean/model.todo.js';
export class ControllerPageTodo {
@Use()
$$modelTodo: ModelTodo;
}
- Inject Model Bean instance:
$$modelTodo
demo-todo/src/page/todo/render.tsx
typescript
export class RenderTodo {
render() {
const todos = this.$$modelTodo.select();
return (
<div>
<div>isLoading: {todos.isLoading}</div>
<div>
{todos.data?.map(item => {
return <div>{item.title}</div>;
})}
</div>
</div>
);
}
}
- Invoke
select
method to obtain the Query object- The render method will be executed multiple times, and repeated calls to the
select
method return the same Query object
- The render method will be executed multiple times, and repeated calls to the
- Directly use the state and data of the Query object
How to support SSR
In SSR mode, we need to use asynchronous data like this: load the state data on the server, and then render it into an HTML string through the render method. The state data and HTML string will be sent to the client at the same time, and the client will still use the same state data when hydrating so as to maintain state consistency
To implement the above logic, only one step is required in Zova Model:
demo-todo/src/page/todo/controller.ts
typescript
import { ModelTodo } from '../../bean/model.todo.js';
export class ControllerPageTodo {
@Use()
$$modelTodo: ModelTodo;
protected async __init__() {
const queryTodos = this.$$modelTodo.select();
await queryTodos.suspense();
if (queryTodos.error) throw queryTodos.error;
}
}
- Just invoke
suspense
in the__init__
method to wait for asynchronous data loading to complete
Data Query: get
How to define
typescript
@Model()
export class ModelTodo {
get(params?: ServiceTodoGetParams) {
if (!params) return undefined;
return this.$useQueryExisting({
queryKey: ['get', params.id],
queryFn: async () => {
return this.scope.service.todo.get(params);
},
});
}
}
- Invoke
$useQueryExisting
to create a Query object - Pass in
queryKey
to ensure the uniqueness of the local cache- Since it is a single piece of data, the key field value of the entry needs to be specified
- Pass in
queryFn
and call this function at the appropriate time to obtain server data
How to use
typescript
export class RenderTodo {
render() {
const params = { id: '1' };
const todo = this.$$modelTodo.get(params);
return (
<div>
<div>todo title: {todo?.data?.title}</div>
<div>{todo?.error?.message}</div>
</div>
);
}
}
- Invoke
$$modelTodo.get()
to get the Query object- Repeated calls to this method return the same Query object
- Directly use the state and data of the Query object
How to support SSR
Same as select
Data Mutation: insert
How to define
typescript
@Model()
export class ModelTodo {
insert() {
return this.$useMutationExisting<void, ServiceTodoIntertParams>({
mutationKey: ['insert'],
mutationFn: async params => {
return this.scope.service.todo.insert(params);
},
onSuccess: () => {
this.$invalidateQueries({ queryKey: ['select'] });
},
});
}
}
- Invoke
$useMutationExisting
to create a Mutation object - Pass in
mutationKey
to ensure the uniqueness of the local cache - Pass in
mutationFn
to perform the mutation operation - Listen to the
onSuccess
method, when the data is created successfully, set theselect
query to invalid in order to re-fetch the data
How to use
typescript
async addTodo() {
const todo = {
id: this.app.meta.util.uuid(),
title: this.newTitle,
done: false,
};
await this.$$modelTodo.insert().mutateAsync(todo);
}
- Prepare new data todo
- Invoke
$$modelTodo.insert()
to obtain the Mutation object- Repeated calls to this method return the same Mutation object
- Directly use the
mutateAsync
method of the Mutation object to complete asynchronous operations
Data Mutation: update
How to define
typescript
@Model()
export class ModelTodo {
update() {
return this.$useMutationExisting<void, ServiceTodoUpdateParams>({
mutationKey: ['update'],
mutationFn: async params => {
return this.scope.service.todo.update(params);
},
onSuccess: (_data, params) => {
this.$invalidateQueries({ queryKey: ['select'] });
this.$invalidateQueries({ queryKey: ['get', params.id] });
},
});
}
}
- Invoke
$useMutationExisting
to create a Mutation object - Pass in
mutationKey
to ensure the uniqueness of the local cache - Pass in
mutationFn
to perform the mutation operation - Listen to the
onSuccess
method, when the data is created successfully, set theselect
andget
queries to invalid in order to re-fetch the data
How to use
typescript
async completeTodo(item: ServiceTodoEntity) {
const todo = { ...item, done: true };
await this.$$modelTodo.update().mutateAsync(todo);
}
- Prepare data todo
- Invoke
$$modelTodo.update()
to obtain the Mutation object- Repeated calls to this method return the same Mutation object
- Directly use the
mutateAsync
method of the Mutation object to complete asynchronous operations
Data Mutation: delete
How to define
typescript
@Model()
export class ModelTodo {
delete() {
return this.$useMutationExisting<void, ServiceTodoDeleteParams>({
mutationKey: ['delete'],
mutationFn: async params => {
return this.scope.service.todo.delete(params);
},
onSuccess: (_data, params) => {
this.$invalidateQueries({ queryKey: ['select'] });
this.$invalidateQueries({ queryKey: ['get', params.id] });
},
});
}
}
- Invoke
$useMutationExisting
to create a Mutation object - Pass in
mutationKey
to ensure the uniqueness of the local cache - Pass in
mutationFn
to perform the mutation operation - Listen to the
onSuccess
method, when the data is created successfully, set theselect
andget
queries to invalid in order to re-fetch the data
How to use
typescript
async deleteTodo(item: ServiceTodoEntity) {
await this.$$modelTodo.delete().mutateAsync({ id: item.id });
}
- Invoke
$$modelTodo.delete()
to obtain the Mutation object- Repeated calls to this method return the same Mutation object
- Directly use the
mutateAsync
method of the Mutation object to complete asynchronous operations