核心原理:
判断有没有触底:scrollTop(内容高度) + clientHeight(根节点的高度) = scrollHeight(滚动条距离顶部的高度)
但如果每次都是完全到达底部后再触发刷新事件的话,用户体验感不佳,所以可以增加偏移量,在即将触底的时候触发事件:
scrollTop(内容高度) + clientHeight(根节点的高度) >= scrollHeight(滚动条距离顶部的高度)- offset(偏移量)
实现下拉刷新的hook:
import _ from 'lodash';
import { useEffect, useState } from 'react';
const OFFSET = 50; // 偏移量
export const useDownLoad = (hasMore = false, loadMore = () => {}) => {
const [tips, setTips] = useState(''); // 页面提示信息
useEffect(() => {
window.onscroll = _.debounce(async () => {
const { clientHeight, scrollTop } = document.documentElement;
const { scrollHeight } = document.body;
if (hasMore && clientHeight + scrollTop >= scrollHeight - OFFSET) {
setTips('加载中...');
await loadMore();
setTips('加载完成');
setTimeout(() => {
setTips('');
}, 1000);
}
}, 500);
return () => {
window.onscroll = null;
};
}, [hasMore]);
return { tips };
};
无限滚动组件:
import React, { memo } from 'react';
import { useDownLoad } from './hooks';
interface IProps {
hasMore: boolean; // 是否还有更多数据
loadMore: () => Promise<unknown>; // 加载更多数据方法
}
const InfiniteScroll = memo(({ hasMore, loadMore }: IProps) => {
const { tips } = useDownLoad(hasMore, loadMore);
return <div>{hasMore ? tips : '没有更多数据了'}</div>;
});
export default InfiniteScroll;
模拟使用无限滚动:
import { memo } from 'react';
import InfiniteScroll from '@/components/InfiniteScroll';
const Component = memo(() => {
// 模拟无限滚动
const loadMoreHandler = () =>
new Promise((resole) => {
setTimeout(() => {
resole(1);
}, 1000);
});
return (
<div>
{/* 内容... */}
<InfiniteScroll hasMore={true} loadMore={loadMoreHandler} />
</div>
);
});
export default Component;