这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情。
大家好,我是小杜杜,在移动端,我们常常会用懒加载去处理数据,这是接口通常会弄分页,前端需要传入不同的页数(page) 和 一页多少条数据(pageSize) 来掉取数据,然后依次展示即可~
条件
当你需要 一个分页的接口,展示列表的数据,需要懒加载,但不想麻烦处理接口,处理数据、处理样式的时候。 可以尝试用用这个组件
大体思路
在这里我们需要使用 ant-mobile的 infiniteScroll这个组件,我们在此基础上结合接口封装,达到列表懒加载
先说说没有这个组件时候,我通常是如何处理的:在用户拉动列表到底部的时候,监听滚动条的高度,当可视区域的高度 + 滚动条滚动的高度 > 滚动内容高度 的时候做判断就OK了
const onScroll = () => {
let clientHeight = scrollRef.current.clientHeight; //可视区域高度
let scrollTop = scrollRef.current.scrollTop; //滚动条滚动高度
let scrollHeight = scrollRef.current.scrollHeight; //滚动内容高度
if((clientHeight+scrollTop + 100) >(scrollHeight) && curPage <= allNumber){
......
}
}
<div onScroll={onScroll} ref={scrollRef}>
这里最好多加入点像素,以防有些时候触发不了,题外话~~~
怎样将 infiniteScroll 与 接口结合起来,形成 ScrollList 组件
首先关于 infiniteScroll 的 Api 非常简单,只有简单的三个属性
- loadMore: 加载更多的回调函数
- hasMore: 是否还有更多内容
- threshold: 触发加载事件的滚动触底距离阈值,单位为像素
我可以看到,最主要的就是 loadMore 这个参数,他是这个组建的核心,所以需要围绕这个属性去修改
我们通过传参来选择接口返回的数据,然后将数据累加,并判断数据的合 是否等于总数据,然后将数据返回出去,这是返回的data就可循环做一写操作,通过 childrenNode返回节点就ok了
同时也可以通过 LoadingNode来控制 加载时的样式 NoneNode来控制加载完成时的样式
代码案例
使用方法
import { useState, useEffect } from 'react';
import { ScrollList } from '@/components';
import { List } from 'antd-mobile'
import { Title } from '@/pages/commonPage'
import { getScrollList } from './services'
const Index = () => {
useEffect(() => {
}, [])
return (
<div style={{ padding: '6px 12px'}}>
<ScrollList
onRequest={getScrollList}
payload={{
sizeName: 'size',
sizeNumber: 20
}}
childrenNode={(data:any) => {
return <>
<div style={{ padding: 12 }}>展示数量/总数量:{data.list.length}/{data.res.all}</div>
<List>
{
data.list.map((item:any, index:any) => (
<List.Item key={index}>{item.name}</List.Item>
))
}
</List>
</>
}}
>
</ScrollList>
</div>
)
}
export default Index;
详细代码
import React, { useState } from 'react';
import { InfiniteScroll, Loading } from 'antd-mobile'
import { IndexProps } from './interface.d'
import { useReactive } from 'ahooks';
/**
* @module 无限滚动,列表懒加载
*/
function setWait(number:number) {
return new Promise((resolve:any) => {
setTimeout(() => {
resolve();
}, number);
});
}
const Index:React.FC<IndexProps> = ({ onRequest, payload, calcData, childrenNode, LoadingNode, NoneNode, threshold = 250, wait = 1000}) => {
let [node, setNode] = useState<React.ReactNode>(<></>)
const state = useReactive<any>({
hasMore: true,
page: 1,
number: 0,
data: []
})
const loadMore = async () => {
await setWait(wait)
let params:any = {}
params[payload?.pageName || 'page'] = state.page;
params[payload?.sizeName || 'pageSize'] = payload?.sizeNumber || 10;
const res = await onRequest(calcData ? { ...calcData(), ...payload} : params)
const number = state.number + res[payload?.list || 'list'].length;
const data = [...state.data, ...res[payload?.list || 'list']]
delete res[payload?.list || 'list']
setNode(childrenNode({ list: data, res }))
state.hasMore = false
state.number = number
state.data = data
if(res[payload?.all || 'all'] <= number){
state.hasMore = false
}else{
state.hasMore = true
state.page++
}
}
const InfiniteScrollContent = ({ hasMore }: { hasMore?: boolean }) => {
return (
<>
{hasMore ? LoadingNode ? (<>{LoadingNode}</>) : (
<>
<span>加载中</span>
<Loading />
</>
) : NoneNode ? (<>{NoneNode}</>) : (
<span>--- 我是有底线的 ---</span>
)}
</>
)
}
return (
<>
{node}
<InfiniteScroll loadMore={loadMore} hasMore={state.hasMore} threshold={threshold}>
<InfiniteScrollContent hasMore={state.hasMore} />
</InfiniteScroll>
</>
);
}
export default Index;
End
将请求和处理函数封装在一起,可以有效地进行开发,进行二次封装更好的适配于项目,喜欢的小伙伴点个 Star 支持下吧~