preface
In the background process of development and management, there must be many pages to add, delete, change and query, and most of the logic of these pages are the same, such as the basic functions of obtaining list data, paging, and filtering. The difference is the data items presented. There are also some operation buttons.
When there are only 1 or 2 pages at the beginning, most developers may directly copy another copy of the previous page code. With the progress of the project, the number of similar pages may become more and more, which directly leads to higher and higher coupling of project code.
This is one of the main reasons why some reusable functions or components in the project should be separated
Next, we encapsulate a general useList
, which is adapted to most of the list pages for addition, deletion, modification and query, so that you can complete tasks faster and more efficiently and leave on time ~
Antecedent knowledge
Coding
We need to extract some common parameters and functions and encapsulate them into a common hook
. It is easier to reuse the same functions in other pages later.
Define the paging data necessary for a list page
export default function useList() {
// isLoading
const loading = ref(false);
// current page number
const curPage = ref(1);
// list total
const total = ref(0);
// current page size
const pageSize = ref(10);
}
How to get List Data
Think about it. Let the useList
function receive a listRequestFn
parameter for requesting data in the list.
Define a list
variable to store the data content returned from the network request. Since the list data type cannot be directly determined internally, the list data type can be provided externally in a generic way.
export default function useList<ItemType extends Object>(
listRequestFn: Function
) {
// ignore other code
const list = ref<ItemType[]>([]);
}
Create a loadData
function in useList
to call the data acquisition function. This function receives a parameter to obtain the data of the specified number of pages (optional, default to the value of curPage
).
- Execution process
- Set the loading status
- Call the external incoming function to assign the acquired data to
list
andtotal
- Close the loading state
The async/await syntax is used here. Assume that a request error or a deconstruction error occurs, the catch code block will be followed, and then the loading state will be closed
Note that whether the number and type of parameters received by the incoming listRequestFn function correspond normally
Please adjust according to the actual situation
export default function useList<ItemType extends Object>(
listRequestFn: Function
) {
// ignore other code
// data list
const list = ref<ItemType[]>([]);
// filter data
// get data
const loadData = async (page = curPage.value) => {
// Open Load Status
loading.value = true;
try {
const {
data,
meta: { total: count },
} = await listRequestFn(pageSize.value, page);
list.value = data;
total.value = count;
} catch (error) {
console.log("request error", "error");
} finally {
// Close the loading state
loading.value = false;
}
};
}
Don't forget, there is also paging to be handled
Use the watch
function to listen for data. When the values of curPage
and pageSize
change, call the loadData
function to obtain new data.
export default function useList<ItemType extends Object>(
listRequestFn: Function
) {
// ignore other code
// watch page data
watch([curPage, pageSize], () => {
loadData(curPage.value);
});
}
Now the basic list data acquisition is realized
Implement Data Filters
In a huge data list, data filtering is an essential function
Generally, I define the filter criteria field in a ref
, and throw the ref
to the request function when requesting.
In the useList function, the second parameter receives a filterOption
object, which corresponds to the filter condition field in the list.
Adjust the loadData
function and pass in the filterOption
object to the request function
Note whether the number and type of parameters received by the incoming listRequestFn function correspond normally
Please adjust according to the actual situation
export default function useList<
ItemType extends Object,
FilterOption extends Object
>(listRequestFn: Function, filterOption: Ref<Object>) {
const loadData = async (page = curPage.value) => {
loading.value = true;
try {
const {
data,
meta: { total: count },
} = await listRequestFn(pageSize.value, page, filterOption.value);
list.value = data;
total.value = count;
} catch (error) {
console.log("request error", "error");
} finally {
// Close the loading state
loading.value = false;
}
};
}
Note that the filterOption parameter type requires a ref type, otherwise the responders will be lost and will not work properly
Clear Filter Fields
In the page, there is a reset button to clear the filter conditions. This repeated action can be handled by the reset function.
Use Reflect to set all values to undefined
and request data again.
What is Reflect? Take a look at this article Reflect Mapping Object
export default function useList<
ItemType extends Object,
FilterOption extends Object
>(listRequestFn: Function, filterOption: Ref<Object>) {
const reset = () => {
if (!filterOption.value) return;
const keys = Reflect.ownKeys(filterOption.value);
filterOption.value = {} as FilterOption;
keys.forEach((key) => {
Reflect.set(filterOption.value!, key, undefined);
});
loadData();
};
}
export function
In addition to viewing data, some interfaces also need to have the function of exporting data (such as exporting csv and excel files). We also write the export function to useList
In general, the export function calls the export Api
provided by the backend to obtain a file download address. Similar to the loadData
function, the exportRequestFn
function is obtained externally to call the 'Api`
In the function, add an exportFile
function to call it.
`typescript
export default function useList<
ItemType extends Object,
FilterOption extends Object
(
listRequestFn: Function,
filterOption: Ref,
exportRequestFn?: Function
) {
// ignore other code
const exportFile = async () => {
if (!exportRequestFn) {
throw new Error("Please provide exportRequestFn function");
}
if (typeof exportRequestFn !== "function") {
throw new Error("exportRequestFn must be a function);
}
try {
const {
data: { link },
} = await exportRequestFn(filterOption.value);
window.open(link);
} catch (error) {
console.log("export error", "error");
}
};
}
`Note that whether the number and type of parameters received by the imported exportRequestFn function correspond normally
Please adjust according to the actual situation
Optimization
Now, the entire useList
has met the needs of the page, with the functions of obtaining data, filtering data, exporting data, and paging
There are also some details. The catch
code fragments in the try.. Catch
in all the above codes have not been processed at all, just a simple console. log
Provide hook
In useList
, add an Options object parameter, which is used to execute the specified hook function and output message content when the function succeeds or fails.
Define Options Type
`typescript
export interface MessageType {
GET_DATA_IF_FAILED?: string;
GET_DATA_IF_SUCCEED?: string;
EXPORT_DATA_IF_FAILED?: string;
EXPORT_DATA_IF_SUCCEED?: string;
}
export interface OptionsType {
requestError?: () => void;
requestSuccess?: () => void;
message: MessageType;
}
export default function useList<
ItemType extends Object,
FilterOption extends Object
(
listRequestFn: Function,
filterOption: Ref,
exportRequestFn?: Function,
options? :OptionsType
) {
// ...
}
`
Set Options
default value
`typescript
const DEFAULT_MESSAGE = {
GET_DATA_IF_FAILED: "Failed to get list data",
EXPORT_DATA_IF_FAILED: "Failed to export data",
};
const DEFAULT_OPTIONS: OptionsType = {
message: DEFAULT_MESSAGE,
};
export default function useList<
ItemType extends Object,
FilterOption extends Object
(
listRequestFn: Function,
filterOption: Ref,
exportRequestFn?: Function,
options = DEFAULT_OPTIONS
) {
// ...
}
`It is recommended to set the default failure message when there is no transfer hook
Optimize loadData
, exportFile
functions
Encapsulating message method based on element ui
`typescript
import { ElMessage, MessageOptions } from "element-plus";
export function message(message: string, option?: MessageOptions) {
ElMessage({ message, ...option });
}
export function warningMessage(message: string, option?: MessageOptions) {
ElMessage({ message, ...option, type: "warning" });
}
export function errorMessage(message: string, option?: MessageOptions) {
ElMessage({ message, ...option, type: "error" });
}
export function infoMessage(message: string, option?: MessageOptions) {
ElMessage({ message, ...option, type: "info" });
}
`
loadData function
typescript
const loadData = async (page = curPage.value) => {
loading.value = true;
try {
const {
data,
meta: { total: count },
} = await listRequestFn(pageSize.value, page, filterOption.value);
list.value = data;
total.value = count;
// execute succeed function
options?.message?.GET_DATA_IF_SUCCEED &&
message(options.message.GET_DATA_IF_SUCCEED);
options?.requestSuccess?.();
} catch (error) {
options?.message?.GET_DATA_IF_FAILED &&
errorMessage(options.message.GET_DATA_IF_FAILED);
// execute failed function
options?.requestError?.();
} finally {
loading.value = false;
}
};
exportFile function
typescript
const exportFile = async () => {
if (!exportRequestFn) {
throw new Error("Please provide exportRequestFn function");
}
if (typeof exportRequestFn !== "function") {
throw new Error("ExportRequestFn must be a function");
}
try {
const {
data: { link },
} = await exportRequestFn(filterOption.value);
window.open(link);
// show message
options?.message?.EXPORT_DATA_IF_SUCCEED &&
message(options.message.EXPORT_DATA_IF_SUCCEED);
// execute succeed function
options?.exportSuccess?.();
} catch (error) {
// show message
options?.message?.EXPORT_DATA_IF_FAILED &&
errorMessage(options.message.EXPORT_DATA_IF_FAILED);
// execute failed function
options?.exportError?.();
}
};
Usage of useList
`vue
v-model="filterOption.name"
placeholder="筛选指定签名名称"
/>
v-model="filterOption.timeRange"
type="daterange"
unlink-panels
range-separator="到"
start-placeholder="开始时间"
end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm"
value-format="YYYY-MM-DD HH:mm"
/>
筛选
重置
{{ scope.row.name }}
{{ scope.row.mobile || "未绑定手机号码" }}
{{ scope.row.email || "未绑定邮箱地址" }}
>详情
>
v-model:page-size="pageSize"
background
layout="sizes, prev, pager, next"
:total="total"
:page-sizes="[10, 30, 50]"
/>
import { UserInfoApi } from "@/network/api/User";
import useList from "@/lib/hooks/useList/index";
const filterOption = ref<UserInfoApi.FilterOptionType>({});
const {
list,
loading,
reset,
filter,
curPage,
pageSize,
reload,
total,
loadData,
} = useList<UserInfoApi.UserInfo[], UserInfoApi.FilterOptionType>(
UserInfoApi.list,
filterOption
);
`
The complete code of the useList
in this article is shown in
https://github.com/QC2168/snippets/tree/main/useList
💡 If you have better suggestions for this hook
, welcome topr
or leave a message in the comment area
In addition, in order to save time in searching for encapsulated code fragments and improve work efficiency in daily development 🐟 Time++), this warehouse
It also stores some code fragments encapsulated by the third party ✨, Convenient for everyone 😄😄 (Continuously updating~~)
Top comments (0)