手写H5简易无限滚动功能

205 阅读1分钟

核心原理:

判断有没有触底: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;