您现在的位置是:亿华云 > 人工智能

列表页常见的 Hook 封装,你知道几个?

亿华云2025-10-05 01:15:05【人工智能】4人已围观

简介列表页常见元素对于一些后台管理系统,典型的列表页包括筛选表单项、Table表格、Pagination分页这三部分。针对使用 Antd 的系统,在 ahooks 中主要是通过 useAntdTable

列表页常见元素

对于一些后台管理系统,列表典型的页常列表页包括筛选表单项、Table表格、封装Pagination分页这三部分。知道

针对使用 Antd 的列表系统,在 ahooks 中主要是页常通过 useAntdTable 和 usePagination 这两个 hook 来封装。

列表页常见的 Hook 封装,你知道几个?

usePagination

usePagination 基于 useRequest 实现,封装封装了常见的知道分页逻辑。

列表页常见的 Hook 封装,你知道几个?

首先通过 useRequest 处理请求,列表service 约定返回的页常数据结构为 { total: number, list: Item[] }。

列表页常见的 Hook 封装,你知道几个?

其中 useRequest 的封装 defaultParams 参数第一个参数为 { current: number, pageSize: number }。并根据请求的知道参数以及返回的 total 值,得出总的列表页数。

还有 refreshDeps 变化,页常会重置 current 到第一页「changeCurrent(1)」,封装并重新发起请求,一般你可以把 pagination 依赖的条件放这里。

const usePagination = (

service: Service,

options: PaginationOptions= { },

) => {

const { defaultPageSize = 10, ...rest } = options;

// service 返回的数据结构为 { total: number, list: Item[] }

const result = useRequest(service, {

// service 的第一个参数为 { current: number, pageSize: number }

defaultParams: [{ current: 1, pageSize: defaultPageSize }],

// refreshDeps 变化,会重置 current 到第一页,并重新发起请求,一般你可以把 pagination 依赖的条件放这里

refreshDepsAction: () => {

// eslint-disable-next-line @typescript-eslint/no-use-before-define

changeCurrent(1);

},

...rest,

});

// 取到相关的亿华云计算请求参数

const { current = 1, pageSize = defaultPageSize } = result.params[0] || { };

// 获取请求结果,total 代表数据总条数

const total = result.data?.total || 0;

// 获取到总的页数

const totalPage = useMemo(() => Math.ceil(total / pageSize), [pageSize, total]);

}

重点看下 onChange 方法:

入参分别为当前页数以及当前每一页的最大数量。根据 total 算出总页数。获取到所有的参数,执行请求逻辑。当修改当前页或者当前每一页的最大数量的时候,直接调用 onChange 方法。// c,代表 current page

// p,代表 page size

const onChange = (c: number, p: number) => {

let toCurrent = c <= 0 ? 1 : c;

const toPageSize = p <= 0 ? 1 : p;

// 根据 total 算出总页数

const tempTotalPage = Math.ceil(total / toPageSize);

// 假如此时总页面小于当前页面,需要将当前页面赋值为总页数

if (toCurrent > tempTotalPage) {

toCurrent = Math.max(1, tempTotalPage);

}

const [oldPaginationParams = { }, ...restParams] = result.params || [];

// 重新执行请求

result.run(

// 留意参数变化,主要是当前页数和每页的总数量发生变化

{

...oldPaginationParams,

current: toCurrent,

pageSize: toPageSize,

},

...restParams,

);

};

const changeCurrent = (c: number) => {

onChange(c, pageSize);

};

const changePageSize = (p: number) => {

onChange(current, p);

};

最后返回请求的结果以及 pagination 字段,包含所有分页信息。另外还有操作分页的函数。

return {

...result,

// 会额外返回 pagination 字段,包含所有分页信息,及操作分页的函数。

pagination: {

current,

pageSize,

total,

totalPage,

onChange: useMemoizedFn(onChange),

changeCurrent: useMemoizedFn(changeCurrent),

changePageSize: useMemoizedFn(changePageSize),

},

} as PaginationResult<TData, TParams>;

小结:usePagination 默认用法与 useRequest 一致,但内部封装了分页请求相关的逻辑。返回的结果多返回一个 pagination 参数,包含所有分页信息,服务器租用及操作分页的函数。

缺点就是对 API 请求参数有所限制,比如入参结构必须为 { current: number, pageSize: number },返回结果为 { total: number, list: Item[] }。

useAntdTable

useAntdTable 基于 useRequest 实现,封装了常用的 Ant Design Form 与 Ant Design Table 联动逻辑,并且同时支持 antd v3 和 v4。

首先调用 usePagination 处理分页的逻辑。

const useAntdTable = (

service: Service,

options: AntdTableOptions= { },

) => {

const {

// form 实例

form,

// 默认表单选项

defaultType = simple,

// 默认参数,第一项为分页数据,第二项为表单数据。[pagination, formData]

defaultParams,

manual = false,

// refreshDeps 变化,会重置 current 到第一页,并重新发起请求。

refreshDeps = [],

ready = true,

...rest

} = options;

// 对分页的逻辑进行处理

// 分页也是对 useRequest 的再封装

const result = usePagination(service, {

manual: true,

...rest,

});

// ...

}

然后处理列表页筛选 Form 表单的逻辑,这里支持 Antd v3 和 Antd v4 版本。

// 判断是否为 Antd 的第四版本

const isAntdV4 = !!form?.getInternalHooks;

获取当前表单值,form.getFieldsValue 或者 form.getFieldInstance:

// 获取当前的 from 值

const getActivetFieldValues = () => {

if (!form) {

return { };

}

// antd 4

if (isAntdV4) {

return form.getFieldsValue(null, () => true);

}

// antd 3

const allFieldsValue = form.getFieldsValue();

const activeFieldsValue = { };

Object.keys(allFieldsValue).forEach((key: string) => {

if (form.getFieldInstance ? form.getFieldInstance(key) : true) {

activeFieldsValue[key] = allFieldsValue[key];

}

});

return activeFieldsValue;

};

校验表单逻辑 form.validateFields:

// 校验逻辑

const validateFields = (): Promise> => {

if (!form) {

return Promise.resolve({ });

}

const activeFieldsValue = getActivetFieldValues();

const fields = Object.keys(activeFieldsValue);

// antd 4

// validateFields 直接调用

if (isAntdV4) {

return (form.validateFields as Antd4ValidateFields)(fields);

}

// antd 3

return new Promise((resolve, reject) => {

form.validateFields(fields, (errors, values) => {

if (errors) {

reject(errors);

} else {

resolve(values);

}

});

});

};

重置表单 form.setFieldsValue:

// 重置表单

const restoreForm = () => {

if (!form) {

return;

}

// antd v4

if (isAntdV4) {

return form.setFieldsValue(allFormDataRef.current);

}

// antd v3

const activeFieldsValue = { };

Object.keys(allFormDataRef.current).forEach((key) => {

if (form.getFieldInstance ? form.getFieldInstance(key) : true) {

activeFieldsValue[key] = allFormDataRef.current[key];

}

});

form.setFieldsValue(activeFieldsValue);

};

修改表单类型,支持 simple 和 advance。初始化的表单数据可以填写 simple 和 advance 全量的表单数据,网站模板开发者可以根据当前激活的类型来设置表单数据。修改 type 的时候会重置 form 表单数据。

const changeType = () => {

// 获取当前表单值

const activeFieldsValue = getActivetFieldValues();

// 修改表单值

allFormDataRef.current = {

...allFormDataRef.current,

...activeFieldsValue,

};

// 设置表单类型

setType((t) => (t === simple ? advance : simple));

};

// 修改 type,则重置 form 表单数据

useUpdateEffect(() => {

if (!ready) {

return;

}

restoreForm();

}, [type]);

_submit 方法:对 form 表单校验后,根据当前 form 表单数据、分页等筛选条件进行对表格数据搜索。

const _submit = (initPagination?: TParams[0]) => {

setTimeout(() => {

// 先进行校验

validateFields()

.then((values = { }) => {

// 分页的逻辑

const pagination = initPagination || {

pageSize: options.defaultPageSize || 10,

...(params?.[0] || { }),

current: 1,

};

// 假如没有 form,则直接根据分页的逻辑进行请求

if (!form) {

// @ts-ignore

run(pagination);

return;

}

// 获取到当前所有 form 的 Data 参数

// record all form data

allFormDataRef.current = {

...allFormDataRef.current,

...values,

};

// @ts-ignore

run(pagination, values, {

allFormData: allFormDataRef.current,

type,

});

})

.catch((err) => err);

});

};

另外当表格触发 onChange 方法的时候,也会进行请求:

// Table 组件的 onChange 事件

const onTableChange = (pagination: any, filters: any, sorter: any) => {

const [oldPaginationParams, ...restParams] = params || [];

run(

// @ts-ignore

{

...oldPaginationParams,

current: pagination.current,

pageSize: pagination.pageSize,

filters,

sorter,

},

...restParams,

);

};

初始化的时候,会根据当前是否有缓存的数据,有则根据缓存的数据执行请求逻辑。否则,通过 manual 和 ready 判断是否需要进行重置表单后执行请求逻辑。

// 初始化逻辑

// init

useEffect(() => {

// if has cache, use cached params. ignore manual and ready.

// params.length > 0,则说明有缓存

if (params.length > 0) {

// 使用缓存的数据

allFormDataRef.current = cacheFormTableData?.allFormData || { };

// 重置表单后执行请求

restoreForm();

// @ts-ignore

run(...params);

return;

}

// 非手动并且已经 ready,则执行 _submit

if (!manual && ready) {

allFormDataRef.current = defaultParams?.[1] || { };

restoreForm();

_submit(defaultParams?.[0]);

}

}, []);

最后,将请求返回的数据通过 dataSource、 pagination、loading 透传回给到 Table 组件,实现 Table 的数据以及状态的展示。以及将对 Form 表单的一些操作方法暴露给开发者。

return {

...result,

// Table 组件需要的数据,直接透传给 Table 组件即可

tableProps: {

dataSource: result.data?.list || defaultDataSourceRef.current,

loading: result.loading,

onChange: useMemoizedFn(onTableChange),

pagination: {

current: result.pagination.current,

pageSize: result.pagination.pageSize,

total: result.pagination.total,

},

},

search: {

// 提交表单

submit: useMemoizedFn(submit),

// 当前表单类型, simple | advance

type,

// 切换表单类型

changeType: useMemoizedFn(changeType),

// 重置当前表单

reset: useMemoizedFn(reset),

},

} as AntdTableResult<TData, TParams>;

很赞哦!(641)