react 项目中 使用 antd 的 Table 实现列表的上拉加载
实现上拉加载 主要是通过计算 判断滚动条是否滚动到底部, 来进行触发加载事件的
需要注意的是 onScrollEvent 方法中所用到的变量, 不能 用 state 和 useState 来控制, 不能监听到他们的变化
元素宽高
(1)clientHeight、clientWidth→元素宽高(height+padding,不包含边框),可以理解为元素可视区域高度
(2)offsetHeight、offectWidth→元素宽高(height+padding+border,包含边框),可以理解为元素的可视高度
(3)scrollHeight、scrollWidth→元素宽高(内容的实际高度+上下padding<如果没有限制div的height,即height是自适应的,那么scrollHeight=clientHeight>)
(4)scrollHeight与offsetHeight的区别: offsetHeight即是自身的高度,scrollHeight是自身的高度+隐藏元素的高度(即是内层元素的offsetHeight)
滚动距离
(1)offsetTop:为容器相对于document的top的绝对偏移---→等于top+margin-top;同理offsetLeft:容器相对于document的left的绝对偏移---→等于left+margin-left
(2)clientTop: 容器内部相对于容器本身的top偏移,实际就是border-width
(3)scrollTop: Y轴的滚动条没有,或滚到最上时是0;Y轴的滚动条滚到最下时是 scrollHeight-clientHeight。滚动时通常只能scrollTop,当scrollTop为 0 到 scrollHeight-clientHeight 是正常的滚动距离,否则就是滚动过头了(手机上的阻尼效果)。
const ListComponent = () => {const [actList, setActList] = useState<any>([]); let lastScrollTop = 0; let pages = 0; let pageNum = 0; let loading = false; useEffect(() => { search('first'); return () => {}; }, []); const onScrollEvent = (e) => { if (loading) { return; } if ( e.target.scrollTop + e.target.clientHeight > e.target.scrollHeight - 50 ) { // 这里去做异步数据加载 const pullDown = e.target.scrollTop - lastScrollTop > 0; if (pullDown && pageNum < pages) { loading = true; pageNum++; dispatchers.getList({ pageNum }).then(() => (loading = false)); } } lastScrollTop = e.target.scrollTop; }; // 多选 const Selection = (row, type) => { let list = [...actList]; if (type) { list.push(row.id); } else { const index = actList.findIndex((m) => m === row.id); list.splice(index, 1); } setActList(list); }; // 查看转编辑 const compile = () => { pageNum = 0; onMore('first'); }; // 搜索 const search = (type: string) => { dispatchers.update({ params: {} }); lastScrollTop = 0; pageNum = 0; onMore(type); }; // 页面初始化获取列表 传 first是需要判断 第一页是否加载完已选中数据, 如果没有需要加载第二页 let sltArr = []; const onMore = async (type: string) => { pageNum++; await dispatchers.getList({ pageNum }).then((res) => { if (type === 'first') { res.list.forEach((m) => { if (m.isGroup) { sltArr.push(m.id); // 选中数据 } }); if (history.location.state?.num / 20 > pageNum) { onMore(type); } else { setActList(sltArr); } } pages = res?.pages || 0; document.getElementsByClassName('ant-table-body')[0].scrollTop = 0; document .getElementsByClassName('ant-table-body')[0] .addEventListener('scroll', onScrollEvent); }); }; return ( <div> <div className={css.act}> 已选择 <span>{actList?.length}</span> 项,总数: {total} 人<span onClick={() => setActList([])}>清空</span> </div> <Table rowSelection={{ onSelect: Selection, hideSelectAll: true, selectedRowKeys: actList, }} size="small" scroll={{ y: 570 }} className={'tableBox'} columns={columns} dataSource={state.list} pagination={false} rowKey="id" /> </div> ); };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义