前言
最近的开发迭代业主需要用到一个 Select 下拉加载功能,但项目组上没有这个组件,Antd Design官网也没有提供现成的,查阅资料也没有发现符合业务的组件,特此自己造轮子封装。
使用场景
注:当下拉框数据较多时,后端采用分页搜索模式,这个时候就可以使用这个组件。
实现思路
想要封装一个好的组件,我们需要考虑它的共用性,既然是下拉加载组件,它应具备以下功能:
- 应该继承 Select 组件的全部属性
- 滚动到底部,数据加载下一页,如果接口返回的条数小于当前设置的条数,就取消滚动加载功能
- 支持自定义 options
…
搭建基本结构
我们先看下后端返回的数据结构:
如果数据结构不一样,可根据后端要求修改:
1 | import { useBoolean, useRequest, useSetState } from 'ahooks' |
这里我使用了 ahooks库
完善功能
架子我们已经搭好了,现在在其添砖加瓦。
判断滚动距离,滚动到底部加载下一页
这里我们用到了 Select组件 的onPopupScroll
API1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 判断是否滚动到底部
const { run: onPopupScroll } = useDebounceFn(
async (e) => {
e.persist();
const { scrollTop, offsetHeight, scrollHeight } = e.target;
// 距离底部多少距离开始加载下一页
if (((scrollTop + offsetHeight) === scrollHeight) && !isEmptyData) {
// 当滚动到底部时,我们下载下一页,同时更新分页状态
setRequestParams({ currentPage: requestParams.currentPage + 1 })
await runAsyncRequestList({ ...requestParams, currentPage: requestParams.currentPage + 1 })
}
},
{ wait: 350 },
);
<Select
showSearch
loading={requestLoading}
filterOption={false}
onPopupScroll={(e) => onPopupScroll(e)}
onSearch={(value: string) => handleSearch(value)}
placeholder="请选择"
onClear={handlerClear}
{...props}
>
</Select>增加搜索功能
组件支持搜索功能,当用户搜索后重新触发接口加载,并重置分页状态,这里还要加一个防抖函数,避免输入过程中频繁触发接口请求:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 重新请求
const initRequest = async (newValue: string) => {
// 搜索重置分页
setRequestParams({ currentPage: 1, searchKey: newValue })
setEmptyDataFalse()
// 重置数据列表
setRequestList([])
await runAsyncRequestList({ ...requestParams, ...extraParams, currentPage: 1, searchKey: newValue })
}
// 搜索回调
const { run: handleSearch } = useDebounceFn(
async (newValue: string) => {
// 重置参数,重新请求
initRequest(newValue)
},
{ wait: 500 },
);增加清空数据回调
当用户搜索的数据只有一条时,选中后清空数据,这时候我们应该重新请求数据:1
2
3
4
5
6
7// 清除内容时的回调
const handlerClear = async () => {
// 当数据不足一页时,重新加载
if (requestList.length < requestParams.pageRows) {
initRequest('')
}
}自定义 options
有时候需求需要自定义内容,如下图:
这时我们还要提供一个 props 给组件自定义,并提供当前的列表数据:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37// 父组件
// 自定义 options
const renderOptions = (ownerHouseList: OwnerHouseProps[]) => {
return (
ownerHouseList?.map((house) => {
return (
<Option
value={house.id}
label={`${house.address}(${house.houseCode})`}
disabled={house.optional === 0}
key={house.id}
>
<Row>
<Col span={24}>{house.houseCode}</Col>
<Col span={24}>盘源地址:{house.address}</Col>
</Row>
</Option>
)
})
)
}
// 子组件
{
customizeOptions ? customizeOptions(requestList) :
requestList?.map((item) => {
return (
<Option
{...item}
value={item[fieldNames.value]}
key={item[fieldNames.value]}
>
{item[fieldNames.label]}
</Option>
)
})
}细节完善
作为开发者,我们还需要考虑组件的共用性:- 接口可能返回的字段不一样
- 下拉滚动到底部多少距离触发
- 一些自定义文案
- 父组件需要调用子组件的请求方法
…
最终我们的代码如下:
1 | /* |
使用方式
1 | import LazyLoadSelect from '@/components/LazyLoadSelect' |
参数说明
参数 | 说明 | 类型 | 默认值 | 是否必传 |
---|---|---|---|---|
apiUrl | 请求接口 | Promise | - | √ |
pageRows | 每页显示条数 | number | 15 | - |
resultField | 后端返回数据字段 | string | list | - |
emptyText | 空数据的显示文案 | string | 没有更多了 | - |
onRef | 绑定子组件实例 | React.RefObject | - | - |
extraParams | 额外参数 | object | {} | - |
manual | 是否手动执行 | boolean | false | - |
distanceBottom | 滚动距离多少触发加载,默认滚动到底部,也就是0 | number | 0 | - |
customizeOptions | 自定义 option | - | - | - |
SelectProps | Antd Select选择器 Props,配置项 | - | - | - |
注意事项
如果接口请求后,下拉框没看到数据,请检查参数
fieldNames
是否配置正确。如果下拉过程出现数据重复等BUG,请检查后端返回的数据
fieldNames.value
是否唯一,且必传,否则组件会出现BUG。如果需要自定义
option
,请使用customizeOptions
属性如果组件的功能不符合您的业务场景,请联系我拓展完善。
让我们看看最终效果:
如果此文章对你有帮助,请帮我点个赞吧!
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Cyan!
评论
您无需删除空行,直接评论以获取最佳展示效果