Skip to main content
Version: v3

Pagination request strategy

Strategy type

use hook

Before using extended hooks, make sure you are familiar with the basic use of alova.

A hook designed for paging scenarios, it can help you automatically manage paging data, preload data, reduce unnecessary data refreshes, improve fluency by 300%, and reduce coding difficulty by 50%. You can use it in two paging scenarios: pull-down loading and page number flipping. This hook provides rich features to help your application create better performance and more convenient paging functions.

Features

  • Rich and comprehensive paging status;

  • Rich and comprehensive paging events;

  • Automatically obtain the specified paging data by changing page and pageSize;

  • Data cache, no need to repeatedly request list data with the same parameters;

  • Preload the previous and next pages, no need to wait for page turning;

  • Automatically re-acquire the number of pages by monitoring the search conditions;

  • Support adding, editing, and deleting list data;

  • Support refreshing the data of the specified page without resetting;

  • Request-level search anti-shake, no need for self-maintenance;

Usage

Render list data

<template>
<div
v-for="(item, i) in data"
:key="item.id">
<span>{{ item.name }}</span>
<button
:loading="removing.includes(i)"
@click="remove(item)">
Delete
</button>
</div>
<button @click="handlePrevPage">Previous page</button>
<button @click="handleNextPage">Next page</button>
<button @click="handleSetPageSize">Set the number of pages per page</button>
<span>Total {{ pageCount }} pages</span>
<span>Total {{ total }} data</span>
</template>

<script setup>
import { queryStudents } from './api.js';
import { usePagination } from 'alova/client';

const {
// Loading status
loading,

// List data
data,

// Is it the last page
// This parameter can be used to determine whether loading is required when pulling down to load
isLastPage,

// Current page number. Changing this page number will automatically trigger a request
page,

// Number of data items per page
pageSize,

// Number of pages for paging
pageCount,

// Total amount of data
total,

// [3.3.0+]
// action status. This status will be changed only when the corresponding action is triggered. The specific values ​​are as follows:
// empty string: default status
// loading: list data request
// removing: list data deletion
// inserting: list data insertion
// replacing: list data replacement
status,

// [3.3.0+]
// Removing list items
remove,

// [3.3.0+]
// The row index array being removed, used to control the delete button status of the corresponding column
removing,

// [3.3.0+]
// Replace list items
replace,

// [3.3.0+]
// Row being replaced index, used to control the replacement button state of the corresponding column
replacing
} = usePagination(
// Method instance acquisition function, which will receive page and pageSize and return a Method instance
(page, pageSize) => queryStudents(page, pageSize),
{
// Initial data before request (data format returned by the interface)
initialData: {
total: 0,
data: []
},
initialPage: 1, // Initial page number, default is 1
initialPageSize: 10, // Initial number of data per page, default is 10

// [3.3.0+]
actions: {
// When the remove function is called, this action will be triggered and receive the parameters of the remove function
remove: async row => {
// fetch remove interface...
}
}
}
);

// Turn to the previous page, and the request will be automatically sent after the page value changes
const handlePrevPage = () => {
page.value--;
};

// Turn to the next page, the request will be automatically sent after the page value is changed
const handleNextPage = () => {
page.value++;
};

// Change the number of pages per page, the request will be automatically sent after the pageSize value is changed
const handleSetPageSize = () => {
pageSize.value = 20;
};
</script>

Specify pagination data

Each pagination data interface returns a different data structure, so we need to tell usePagination the list data and the total number of items to help us manage the pagination data.

Suppose your pagination interface returns the following data format:

interface PaginationData {
totalNumber: number;
list: any[];
}

At this time, you need to return the list data and the total number of items in the form of a function.

usePagination((page, pageSize) => queryStudents(page, pageSize), {
// ...
total: response => response.totalNumber,
data: response => response.list
});

If you do not specify the total and data callback functions, they will get the data by default in the following way.

const total = response => response.total;
const data = response => response.data;
Note

The data callback function must return a list data, which represents the data set used in paging, and total is mainly used to calculate the current page number. If no number is returned in the total callback function, it will be determined whether the current page is the last page by whether the number of lists requested is less than the pageSize value. This is generally used for pull-down loading.

Enable append mode

By default, the original list data will be replaced when turning pages, while the append mode is to append the next page of data to the bottom of the current list when turning pages. The common usage scenario is to pull down to load more.

usePagination((page, pageSize) => queryStudents(page, pageSize), {
// ...
append: true
});

Preload adjacent page data

In order to provide a better experience for paging, the previous and next pages of the current page will be automatically preloaded when the conditions are met, so that the data can be displayed directly when the user turns the page without waiting. This is the default behavior. If you do not want to preload the data of adjacent pages, you can turn it off in the following way.

usePagination((page, pageSize) => queryStudents(page, pageSize), {
// ...
preloadPreviousPage: false, // Disable preloading of the previous page data
preloadNextPage: false // Disable preloading of the next page data
});
Preloading trigger conditions

When preloading is enabled, the next page will not be loaded blindly. The following two conditions must be met:

  1. Preloading is based on caching. The Method instance used for paging loading must enable caching. By default, get requests will have a 5-minute memory cache. If it is a non-get request or the cache is globally disabled, you also need to set cacheFor in this Method instance to enable caching.

  2. Determine whether there is data on the next page based on the total and pageSize parameters.

In addition to the onSuccess, onError, and onComplete request events, when preloading is triggered, you can also use fetching to get the preloading status, and you can also use onFetchSuccess, onFetchError, and onFetchComplete to listen to the preloading request events.

const {
// Preloading status
fetching,

// Preloading success event binding function
onFetchSuccess,

// Preloading error event binding function
onFetchError,

// Preloading completion event binding function
onFetchComplete
} = usePagination((page, pageSize) => queryStudents(page, pageSize), {
// ...
});

Listening for filtering conditions

Many times, the list needs to be filtered by conditions. At this time, the usePagination status monitoring can be used to trigger a re-request, which is the same as the useWatcher provided by alova.

For example, filtering by student name or student grade.

<template>
<input v-model="studentName" />
<select v-model="clsName">
<option value="1">Class 1</option>
<option value="2">Class 2</option>
<option value="3">Class 3</option>
</select>
<!-- ... -->
</template>

<script setup>
import { ref } from 'vue';
import { queryStudents } from './api.js';
import { usePagination } from 'alova/client';

//Search condition status
const studentName = ref('');
const clsName = ref('');
const {
// ...
} = usePagination(
(page, pageSize) => queryStudents(page, pageSize, studentName.value, clsName.value),
{
// ...
watchingStates: [studentName, clsName]
}
);
</script>

Similar to useWatcher, you can also implement request anti-shake by specifying debounce. For details, please refer to useWatcher's debounce parameter setting.

usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), {
// ...
debounce: 300 // Anti-shake parameter, in milliseconds, can also be set as an array to set the anti-shake time for watchingStates
});

It should be noted that debounce is implemented through request anti-shake in useWatcher. **There are two hidden monitoring states, page and pageSize, at the end of the monitoring state, which can also be set through debounce. **

For example, when watchingStates sets [studentName, clsName], [studentName, clsName, page, pageSize] will be monitored internally, so if you need to set anti-shake for page and pageSize, you can specify [0, 0, 500, 500].

Turn off initialization request

By default, usePagination will initiate a request at initialization, but you can also use immediate to turn it off, and then initiate a request through the send function, or change page or pageSize, as well as watchingStates and other monitoring states.

usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), {
// ...
immediate: false
});

List operation function

usePagination provides a full-featured list operation function, which can achieve the same effect as re-requesting the list without re-requesting the list, greatly improving the interactive experience of the page, and can also trigger the corresponding action to request data and display the operation status.

Insert list item

You can use it to insert a list item to any position in the list, and it will remove the last item after insertion to ensure the same effect as re-requesting the current page data.

/**
* Insert a piece of data
* If index is not passed, it will be inserted at the front by default
* If a list item is passed, it will be inserted after this list item. If the list item is not in the list data, an error will be thrown
* @param item Insert item
* @param indexOrItem Insert position (index)
*/
declare async function insert(item: LD[number], indexOrItem?: number | LD[number]): void;

The following is an example of returning to the first page and inserting a list item in non-append mode (page number flipping scenario):

page.value = 1;
nextTick(() => {
insert(newItem, 0);
});

The following is an example of scrolling to the top after inserting a list item in append mode (pull-down loading scenario):

await insert(newItem, 0);
nextTick(() => {
window.scrollTo(0, {});
});

You can also specify the second parameter of insert as a list item. When the same reference of this list item is found, the inserted item will be inserted after this list item.

await insert(newItem, afterItem);
Note

In order to make the data correct, the insert function call will clear all caches.

insert action

You can also specify the insert callback in actions, which is used to send the insert request, which will be triggered before inserting data.

usePagination({
// ...
actions: {
// row parameter passed from insert function
insert: async row => {
await insertListItem(row);
}
}
});

Remove list items

If the next page has cache, it will use the cache of the next page to add to the end of the list item after removing an item to ensure the same effect as re-requesting the current page data. It behaves the same in append mode and non-append mode.

/**
* Remove a piece of data
* If a list item is passed in, it will be removed. If the list item is not in the list data, an error will be thrown
* @param position The index or list item to be removed
*/
declare async function remove(...positions: Array<number | LD[number]>): void;

You can also specify the parameter of remove as a list item. When the same reference of this list item is found, this list item will be removed.

But in the following two cases, it will re-initiate a request to refresh the data of the corresponding page:

  1. The next page is not cached

  2. The data of the cache list items exceeding the next page is called synchronously and continuously, and the cached data is not enough to supplement the current page list.

Note

In order to make the data correct, the remove function call will clear all caches.

remove action

You can also specify the remove callback in actions, which is used to send a removal request, which will be triggered before removing the data.

usePagination({
// ...
actions: {
// The row parameter is passed from the remove function. If the remove function passes multiple removal items, this callback will be called multiple times
remove: async row => {
await insertListItem(row);
}
}
});

Update data items

When you want to update list items, use this function.

/**
* Replace a piece of data
* When position is passed a number, it means replacing the index. A negative number means counting from the end. When position is passed a list item, it will replace this list item. If the list item is not in the list data, an error will be thrown
* @param item replacement item
* @param position replacement position (index) or list item
*/
declare async function replace(
item: LD extends any[] ? LD[number] : any,
position: number | LD[number]
): void;

You can also specify the second parameter of replace as a list item. When the same reference of this list item is found, this list item will be replaced.

replace action

You can also specify the replace callback in actions, which is used to send a replacement request. It will be triggered before replacing the data.

usePagination({
// ...
actions: {
// row parameter passed from replace function
replace: async row => {
await replaceListItem(row);
}
}
});

Refresh the data of the specified page

When you do not want to update the list items locally after data operation, but re-request the data from the server, you can use refresh to refresh the data of any page without resetting the list data and letting the user start browsing from the first page again.

/**
* Refresh the specified page number data, this function will ignore the cache and force the request to be sent
* If the page number is not passed in, the current page will be refreshed
* If a list item is passed in, the page where the list item is located will be refreshed
* @param pageOrItemPage refreshed page number or list item
* @returns [v3.1.0+] promise containing response data
*/
declare function refresh(pageOrItemPage?: number | LD[number]): Promise<AG['Responded']>;

In append mode, you can specify the parameter of refresh as a list item. When the same reference of this list item is found, the data of the page number of this list item will be refreshed.

Manually update list data

Use the update function to update responsive data, which is similar to useRequest's update. The only difference is that when calling update to update data, the list data is updated instead of the response data. This is useful when manually clearing the list data without re-initiating a request.

// Situation list data
update({
data: []
});

Reset the list

It will clear all caches and reload the first page.

/**
* Reload the list from the first page and clear the cache
* @returns [v3.1.0+]promise instance, indicating whether the reset is successful
*/
declare function reload(): Promise<void>;

Compatible with updateState

You can also use updateState to update the responsive data exported by usePagination.

updateState(listMethod, {
// Update the exported list data, that is, data.
data: oldList => [...oldList, ...newList],
// Update the exported total data
total: oldTotal => oldTotal + newList.length,
// Update exported page data
page: oldPage => oldPage + 1,
// Update exported pageSize data
pageSize: oldPageSize => oldPageSize + 10
});

Click here to viewDetailed usage of updateState.

Since the data type of usePagination is not directly inferred from method, the type needs to be manually specified in updateState.

updateState<Item[]>(listMethod, {
data: oldList => [...oldList, ...newList]
});

API

Hook configuration

Inherits all configurations of useWatcher.

NameDescriptionTypeDefaultVersion
initialPageInitial page numbernumber1-
initialPageSizeInitial number of data per pagenumber10-
watchingStatesState monitoring trigger request, implemented using useWatcherany[][page, pageSize]-
debounceDebounce parameter for state monitoring, implemented using useWatchernumber | number[]--
appendWhether to enable append modebooleanfalse-
dataArray data for specifying paging(response: any) => any[]response => response.data-
totalSpecified total number of data(response: any) => numberresponse => response.total-
preloadPreviousPageWhether to preload the previous page of databooleantrue-
preloadNextPageWhether to preload the next page of databooleantrue-
actionsOperation function callback, used to request the corresponding interfacePaginationAction-3.3.0

PaginationAction

NameDescriptionTypeDefault valueVersion
insertinsert callback function, triggered when the operation function insert is called(item: Row, position: number) => Promise<void>-3.3.0
removeremove callback function, triggered when the operation function remove is called(item: number | Row) => Promise<void>-3.3.0
replacereplace callback function, triggered when the operation function replace is called(item: number | Row, position: number) => Promise<void>-3.3.0

Responsive data

Inherits all responsive data from useWatcher.

NameDescriptionTypeVersion
pageCurrent page number, determined by initialPagenumber-
pageSizeCurrent number of pages per page, determined by initialPageSizenumber-
dataPaginated list array data, configured by dataany[]-
totalTotal number of data, configured by total, can be emptynumber-
pageCountTotal number of pages, calculated by total and pageSizenumber-
isLastPageWhether the current page is the last page, when pageCount has a value, it will be compared with pageCount, otherwise it will be determined by whether the length of the list data is less than pagSizenumber-
fetchingWhether data is being preloadedboolean-
statusaction status. This status will be changed only when the corresponding action is triggered. The specific values ​​are as follows:
empty string: default status;
loading: list data is being requested;
removing: list data is being deleted;
inserting: list data is being inserted;
replacing: list data is being replaced
string3.3.0
removingThe row index array being removed, used to control the delete button status of the corresponding columnnumber[]3.3.0
replacingThe row index being replaced, used to control the replacement button status of the corresponding columnnumber3.3.0

Operation function

Inherits all operation functions of useWatcher.

NameDescriptionFunction ParametersReturn ValueVersion
refreshRefresh the specified page number data. This function will ignore the cache and force the request to be sent. In append mode, a list item can be passed in to indicate the page number where the list item is located.pageOrItemPage: refreshed page number or list itemPromise<AG['Responded']>v3.1.0+
insertInsert a piece of data. If index is not passed in, it will be inserted at the front by default. If a list item is passed in, it will be inserted after this list item. If the list item is not in the list data, an error will be thrown1. item: insert item
2. indexOrItem: insert position (index) or list item, default is 0
--
removeRemove a piece of data. When a number is passed in, it indicates the index to be removed. When position If a list item is passed in, it will be removed. If the list item is not in the list data, an error will be thrown.position: Remove position (index) or list item. Multiple items can be passed in for batch deletion.--
replaceReplace a piece of data. When a number is passed in as the second parameter, it means replacing the index. A negative number means counting from the end. When position is passed in as a list item, it will replace the list item. If the list item is not in the list data, an error will be thrown.1. item: Replacement item
2. position: Replacement position (index) or list item. When a negative number is passed in, it means counting from the end.
--
reloadClear data and re-request the first page of data-Promise<void>v3.1.0+
updateUpdate status data. It is the same as the use hook of alova, but when updating the data field, it updates the list data.newFrontStates: New status data object--

Events

Inherits all events of useWatcher.

NameDescriptionCallback parametersVersion
onFetchSuccessCallback binding function for successful fetchevent: alova successful event object-
onFetchErrorCallback binding function for failed fetchevent: alova failed event object-
onFetchCompleteCallback binding function for completed fetchevent: alova completed event object-