1、自定义Table的hook——useTable
🍒关于实现table表格的需求中,会需要请求数据,分页,刷新,条件搜索等等行为逻辑。为避免代码的繁琐冗余,所以将其封装为一整个hook
a.关于定义useTable
import React, { useEffect, useReducer, useState } from 'react';
import { isUndefined } from "lodash-es";
import useDebounce from "@/hooks/useDebounce";
//默认初始条件
const initData = {
data: [],
count: 0,
totalPage: 0,
conditions: {
pageSize: 10,
pageNum: 1
}
};
const tableHandler = (state, action) => {
const { type, payload = {} } = action;
if (type === 'INIT_DATA') {
return Object.assign({}, state, {
...payload,
});
} else if (type === 'MODIFY_PAGES') {
const { conditions } = state;
const { pageSize = 10, pageNum = 1 } = payload;
return Object.assign({}, state, {
conditions: Object.assign({}, conditions, {
...payload
}),
pageSize,
pageNum
});
} else if (type === 'MODIFY_CONDITION') {
return {
...state,
conditions: {
...state.conditions,
...payload
}
};
} else if (type === 'CONCAT_DATA') {
const { data, conditions } = state;
const { pageSize, pageNum, ...rest } = payload;
return Object.assign({}, state, {
...Object.assign({}, rest, {
data: data.concat(payload.data),
}, pageSize, pageNum
)
});
} else if (type === 'RESET_CONDITIONS') {
return {
...state,
conditions: isUndefined(payload) ? initData.conditions : payload
}
}
return state;
};
const useTable = (
type, //自定义命名
fetchTableData, //api
initConditions = {}, //传入条件
isFetch = true, //可手动控制是否请求的开关
isLoadable = false
) => {
const [isLoading, setIsLoading] = useState(false);
const [tableData, dispatchTableData] = useReducer(tableHandler, Object.assign({},
initData, {
conditions: Object.assign({}, initData.conditions, initConditions),
}), data => {
return data;
});
const { cacheConditions } = useDebounce(tableData.conditions, 300,
'cacheConditions');
useEffect(() => {
if (fetchTableData && isFetch) {
fetchData(cacheConditions);
}
}, [cacheConditions, isFetch]);
//请求api
const fetchData = async conditions => {
setIsLoading(true);
const { pageSize, pageNum, ...rest } = conditions;
fetchTableData({
pageNum,
pageSize,
...rest
}).then(res => {
if (res && res.code === 0 || (res && res.data)) {
const { pageNum } = res;
if (pageNum !== 1 && isLoadable) {
dispatchTableData({
type: 'CONCAT_DATA',
payload: res
});
} else {
dispatchTableData({
type: 'INIT_DATA',
payload: res
});
}
}
setIsLoading(false);
});
};
//翻页动作
const modifyPages = (pageNum, pageSize) => {
dispatchTableData({
type: 'MODIFY_PAGES',
payload: {
pageNum,
pageSize
}
});
};
//搜索动作
const modifyConditions = (conditions) => {
dispatchTableData({
type: 'MODIFY_CONDITION',
payload: conditions
});
};
//强制刷新
const forceRefresh = (pageNum, pageSize = tableData.conditions.pageSize) => {
fetchTableData({
...tableData.conditions,
pageNum: !isUndefined(pageNum) ? pageNum : tableData.pageNum,
pageSize: pageSize,
}).then(res => {
dispatchTableData({
type: 'INIT_DATA',
payload: res
});
});
};
//滚动加载
const onScroll = (e) => {
const {target} = e
const {pageNum, totalPage, pageSize} = tableData
if (target.scrollTop + target.offsetHeight >= target.scrollHeight) {
if (currentPage + 1 > totalPage) {
return
}
modifyPages(pageSize, pageNum + 1)
}
}
//条件重置
const resetConditions = (payload) => {
dispatchTableData({
type: 'RESET_CONDITIONS',
payload
})
}
return type ? {
[`${type}TableData`]: tableData,
[`dispatch${type.split('').map((i, index) => index === 0 ? i.toUpperCase() : i).join('')}TableData`]: dispatchTableData,
modifyPages,
forceRefresh,
modifyConditions,
isLoading,
conditions: cacheConditions,
customFetchData: fetchData,
onScroll,
resetConditions
} : {
tableData,
dispatchTableData,
modifyPages,
forceRefresh,
modifyConditions,
isLoading,
conditions: cacheConditions,
customFetchData: fetchData,
onScroll,
resetConditions
};
};
export default useTable;
b.关于使用useTable
//useTable初始条件:
// 1.可自定义tableData的名字(必填,或为undefined)
// 2.获取table数据的api(必填)
// 3.api参数
const {
xxxTableData,
modifyPages,
forceRefresh,
modifyConditions,
isLoading,
onScroll,//如需滚动加载,将其放到上层dom
} = useTable('xxx', getListApi, {pageSize: 10000});
//解构xxxTableData
const { dataSource, pagination } = useMemo(() => {
const {
data,
conditions: {
pageSize,
pageNum
},
count
} = xxxTableData || {};
return {
dataSource: data,
pagination: {
pageSize,
current: pageNum,
total: count,
},
};
}, [xxxTableData])
const getPage = (pageNum, pageSize) => modifyPages(pageSize, pageNum)
<Table
loading={isLoading}
pagination={{
...pagination,
onChange: getPage,
onShowSizeChange: getPage,
showSizeChanger: true,
showTotal: (total) => 共{total}条,
showQuickJumper: true,
}}
dataSource={dataSource}
columns={columns}
/>
⏰以上可以实现大部分的table操作行为。但在项目中还是遇到些比较刁钻的逻辑,以至于不断将useTable进行改造,我就不补充了。。。希望大家遇不到
2、关于Table单元格改造
🍒在实际需求中会要求单元格的底色可动态变化
a.实现当前列底色动态变化
{
title: '等级',
dataIndex: 'level',
key: 'level',
align: 'left',
isResize: true,
width: 200,
render: (text = {}, record) => {
const { level } = record
return {
props: {
style: {
background: colorEnum[level.toLowerCase()]
}
},
children: <div>{text}</div>
};
}
}
b.实现当前整行底色动态变化
🍒利用Table的rowClassName属性自定义类名
const onClickRowStyle = (record, rowIndex) => {
return 'xxx' + record.level
}
return <Table
rowClassName={(record, rowIndex) => onClickRowStyle(record, rowIndex)}
/>
<Table/>
css部分
.ant-table .ant-table-content .ant-table-tbody {
>tr.xxx-ok {
background: #fff;
}
>tr.xxx-minor {
background: #87CEEB;
}
>tr.xxx-moderate {
background: #FFFF00;
}
>tr.xxx-major {
background: #FFD700;
}
>tr.xxx-critical {
background: #EE2C2C;
}
}
c.项目中遇到要求整行底色不为固定,为可配置情况
1.首先还是自定义类名
const onClickRowStyle = (record, rowIndex) => {
return 'xxx' + record.level
}
return <Table
rowClassName={(record, rowIndex) => onClickRowStyle(record, rowIndex)}
/>
<Table/>
2.再将样式在当前table注入
//此colorMap为api获取,在这里先写死了
const colorMap = {
'ok': '#fff',
'critical':'#fff',
'major':'#fff',
'minor':'#fff',
}
return <>
<Table
rowClassName={(record, rowIndex) => onClickRowStyle(record, rowIndex)}
/>
<style jsx>
{`
:global(.xxx-ok){
background-color:${colorMap['ok']};
}
:global(.xxx-critical){
background-color:${colorMap['critical']};
}
:global(.xxx-major){
background-color:${colorMap['major']};
}
:global(.xxx-minor){
background-color:${colorMap['minor']};
}
`}
</style>
</>