![06cd4cbbe59a16c6b4a81a70bf0ca4b1.png](https://i-blog.csdnimg.cn/blog_migrate/47a27b06c3a240b542d0cc39c6901b80.jpeg)
列表上划加载更多是常见需求,与全量加载的列表相比,动态列表多了当前页码、是否加载完全和结果拼接等工作,这些逻辑散落在组件里,既不直观也容易出错,因此思考能不能有更优雅的实现。
改进思路
对于列表组件来说,关注的数据只有 result
和 isEnd
,前者用来展示数据,后者用来在最后一行显示“加载中”还是“已显示全部结果”,那么,希望有一个 loadData
方法,每次调用都返回最新的 { result, isEnd }
供列表组件使用。而对于当前页面、每页大小等其他信息,由 loadData
自己维护。
具体实现
// services.js
export function createLoadData(url, pageSize = 15) {
const result = []
let pageNum = 0, isEnd = false
return async function () {
if (isEnd) return { result, isEnd }
const res = await axios.get(`${url}?pageNum=${pageNum++}&pageSize=${pageSize}`);
// 也可以用 currentCount === totalCount 或其他条件判断是否结束
if (res.data.length === 0) {
isEnd = true
}
result.push(...res.data.map(item => ({
// 这里进行数据字段适配
id: item.id,
name: item.display_name
})))
return { result, isEnd }
}
}
// ItemList.js
import { createLoadData } from './services'
export default function ItemList() {
const [result, setResult] = useState([])
const [loadEnd, setLoadEnd] = useState(false)
const loadFunc = useRef(noop)
useEffect(() => {
loadFunc.current = createLoadData(url)
loadData();
}, [])
async function loadData() {
const { result, isEnd } = await loadFunc.current();
setResult(result)
setLoadEnd(isEnd)
}
function handleScroll(e) {
const { scrollTop, scrollHeight, clientHeight } = e.target;
// 触底加载
if (scrollTop + clientHeight >= scrollHeight) {
loadData()
}
}
return (
<div className='item-list' onScroll={handleScroll}>
<ul>
{result.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
{loadEnd ? <p>已加载全部数据</p> : <p>加载中</p>}
</div>
)
}