自从 React Hooks 发布以来,对整个前端生态的冲击无疑是巨大的。对我而言,最主要的作用是能够简单的封装高可用的组件了,下面记录一下如何使用 hooks 封装一个 Table 组件。
首先我们看看封装之前的样子:
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { formatDate } from '@/utils/format'
import { getTableDataActionAsync } from '@/store/file/actionCreators'
import { Card, Table } from 'antd'
export default function Table() {
const dispatch = useDispatch()
const id = useSelector((state) => state.id)
let tableData = useSelector((state) => state.table.data)
const tableCount = useSelector((state) => state.table.count)
const isLoading = useSelector((state) => state.ui.isTableLoading)
tableData =
tableData.length &&
tableData.map((item, idx) => ({
...item,
date: formatDate(item.date),
key: idx,
}))
const tableColums = [
{ title: 'name', dataIndex: 'name' },
{ title: 'age', dataIndex: 'age' },
{ title: 'date', dataIndex: 'date' },
]
const handlePageChange = (page) => {
const offset = (page - 1) * 5
getCacheFileActionAsync(id, offset)(dispatch)
}
return (
<Table
columns={tableColums}
dataSource={tableCacheFiles}
bordered
pagination={{
size: 'small',
defaultPageSize: 5,
total: cacheFilesCount,
onChange: (page) => handlePageChange(page),
}}
loading={isLoading}
/>
)
}
可以看到,上面是一个很简单的 antd table,只有最基础的功能,这样的 table,通常在我们的项目中会被大量用到,只有一些小小的区别:
- 展示的数据不同
- 获取数据的接口不同
- 控制 loading 状态的值不同
如果没有加以封装的话,上面的 table,我们在什么地方用到,就得在什么地方写一遍,冗余代码相当多——作为一个
when(me.wake) me.code()
的搬砖机器,我们不应该忍受这样的代码,所以这里尝试对其进行封装。 思路很直接,我们将需要数据抽离出来即可:
import React, { useEffect } from 'react'
import { formatDate } from '@/utils/format'
import { useSelector, useDispatch } from 'react-redux'
export default function useTable(data, count, fn, loading) {
const dispatch = useDispatch()
const id = useSelector((state) => state.id)
let tableData = useSelector((state) => state.table[data])
const tableCount = useSelector((state) => state.table[count])
const isLoading = useSelector((state) => state.ui[loading])
useEffect(() => {
fn(id)(dispatch)
}, [id, dispatch, fn])
tableData =
tableData.length &&
tableData.map((item, idx) => ({
...item,
date: formatDate(item.date),
key: idx,
}))
const tableColumns = [
{ title: 'name', dataIndex: 'name' },
{ title: 'age', dataIndex: 'age' },
{ title: 'date', dataIndex: 'date' },
]
return {
dispatch,
id,
tableData,
tableCount,
tableColumns,
isLoading,
}
}
这样一来,我们在外部直接获取需要的 data 即可:
import React from 'react'
import { useTable } from '@/hooks'
import { getTableDataActionAsync } from '@/store/complete/actionCreators'
export default function Apps() {
const TABLE_DATA = 'data'
const TABLE_COUNT = 'count'
const TABLE_ASYNC = getTableDataActionAsync
const TABLE_LOADING = 'isTableLoading'
const { dispatch, id, tableData, tableCount, tableColumns, isLoading } = useTable(
TABLE_DATA,
TABLE_COUNT,
TABLE_ASYNC,
TABLE_LOADING
)
const handlePageChange = (page) => {
const offset = (page - 1) * 5
TABLE_ASYNC(id, offset)(dispatch)
}
return (
<Table
columns={tableColumns}
dataSource={tableData}
bordered
pagination={{
size: 'small',
defaultPageSize: 5,
total: tableCount,
onChange: (page) => handlePageChange(page),
}}
loading={isLoading}
/>
)
}
当需要处理不同格式的数据,不同数据接口的时候,我们只需要在 useTable 里建好映射表即可:
import React, { useEffect } from 'react'
import { formatDate } from '@/utils/format'
import { useSelector, useDispatch } from 'react-redux'
const tableMap = {
data1: {
data: 'data1',
count: 'data1Count',
loading: 'isData1TableLoading',
},
data2: {
data: 'data2',
count: 'data2Count',
loading: 'isData2TableLoading',
},
}
const tableColumsMap = {
data1: [
{ title: 'name', dataIndex: 'name' },
{ title: 'age', dataIndex: 'age' },
{ title: 'date', dataIndex: 'date' },
]
data2: [
{ title: 'name', dataIndex: 'name' },
{ title: 'age', dataIndex: 'age' },
{ title: 'date', dataIndex: 'date' },
]
}
const tableFormatUtls = {
data1: (data1, idx) => ({
...data1,
date: formatDate(data1.date),
key: idx,
}),
data2: (data2, idx) => ({
...data2,
date: formatDate(data2.date),
key: idx,
}),
}
export default function useTable(model, sotre, fn) {
const dispatch = useDispatch()
const id = useSelector((state) => state.device.id)
const tableStore = useSelector((state) => state[sotre])
const tableDataOrg = tableStore[tableMap[model].data]
const tableCount = tableStore[tableMap[model].count]
const isLoading = useSelector((state) => state.ui[tableMap[model].loading])
useEffect(() => {
fn(id)(dispatch)
}, [id, dispatch, getTableData])
const tableData = tableDataOrg.length && tableDataOrg.map(tableFormatUtls[model])
const tableColumns = tableColumsMap[model]
return {
dispatch,
id,
tableData,
tableCount,
tableColumns,
isLoading,
}
}
这样,我们调用的时候,传入 model 来取对应的值就可以了:
import React from 'react'
import { useTable } from '@/hooks'
import { getTableDataActionAsync } from '@/store/complete/actionCreators'
export default function Apps() {
const TABLE_MODLE = 'data1'
const TABLE_SOTRE = 'table'
const TABLE_ASYNC = getTableDataActionAsync
const { dispatch, id, tableData, tableCount, tableColumns, isLoading } = useTable(
TABLE_MODLE,
TABLE_SOTRE,
TABLE_ASYNC
)
const handlePageChange = (page) => {
const offset = (page - 1) * 5
TABLE_ASYNC(id, offset)(dispatch)
}
return (
<Table
columns={tableColumns}
dataSource={tableData}
bordered
pagination={{
size: 'small',
defaultPageSize: 5,
total: tableCount,
onChange: (page) => handlePageChange(page),
}}
loading={isLoading}
/>
)
}