阅读 208

react+typescript实现无限滚动功能

无限滚动在前端开发中很常见,为了图省事本人在github上尝试了一些开源的无限滚动组件,发现都不好用,而且都许久没有更新了,拿我尝试使用的两个组件【react-infinite-scroll-component,react-infinite-scroller】为例说明一下使用过程中遇到的坑:

1 滚动到底后,会进行无限的请求。
这是由于请求后插入新的元素时,滚动条一直保持在底部,导致无限触发请求。
2.  列表在弹出框(Modal)中时,recentpage无法重置。
进行关闭弹框,搜索列表操作等,next回调参数page仍然为原page,但此时逻辑应该需要重置为1。

基于以上无法解决的问题,我自己实现了一个无限滚动的组件,具体代码入下:

import React, { useEffect, useRef, useState } from 'react';

interface Props {
  children: React.ReactNode;
  // 指定父元素dom,默认为直接父元素
  scrollableTarget?: HTMLElement | null;
  // 当前页,用于确认下一页,必填
  recentPage: number;
  // 加载提示
  loader?: React.ReactNode;
  // 是否有下一个
  hasMore: boolean;
  // 指定加载提示元素的高度
  moreHeight?: number;
  next: (page: number) => void;
}
let preHeight = 0;
const SimpleScroll = (props: Props) => {
  const [parentNode, setparentNode] = useState<HTMLElement>();
  const ref = useRef<HTMLDivElement>(null);
  useEffect(() => {
    // 初始化父节点
    const node = props.scrollableTarget
      ? props.scrollableTarget
      : (ref.current?.parentNode as HTMLElement);
    setparentNode(node);
  }, []);
  useEffect(() => {
    // 重置位置
    if (parentNode && props.recentPage && props.recentPage === 1) {
      parentNode.scrollTop = 0;
    }
    if (parentNode && props.recentPage && props.hasMore) {
      const listener = (e: any) => {
        if (e && e.target) {
          console.log(e.target.scrollTop, e.target.clientHeight, e.target.scrollHeight);
          // 高度突变判断,解决请求后高度变化导致一直请求的bug
          if (preHeight !== e.target.scrollHeight) {
            if (parentNode) {
              parentNode.scrollTop = preHeight;
            }
            preHeight = e.target.scrollHeight;
            return;
          }
          preHeight = e.target.scrollHeight;
          if (
            e.target.scrollTop + e.target.clientHeight + (props.moreHeight || 0) >=
            e.target.scrollHeight
          ) {
            props.next(props.recentPage + 1);
          }
        }
      };
      parentNode.addEventListener('scroll', listener);
      return () => {
        parentNode.removeEventListener('scroll', listener);
      };
    }
  }, [parentNode, props.scrollableTarget, props.recentPage, props.hasMore]);

  return (
    <div ref={ref}>
      {props.children}
      {props.hasMore && props.loader}
    </div>
  );
};

export default SimpleScroll;复制代码

将recentPage作为参数传入,子组件知道当前状态,解决了问题1,通过对上一次的dom高度preHeight的判断来区别是否为插入新元素,解决了问题2。
以下为使用方法:

  useEffect(() => {
    // 出发请求
    if (query) {
      getList(query);
    }
  }, [query]);
  
const getList = (params?: InFireQuery) => {
    ... //列表请求
};
return (
 <SimpleScroll
                next={val => setQuery(pre => ({ ...pre, page: val }))}
                recentPage={fireList.page}
                loader={<div>加载中</div>}
                hasMore={hasMore}>
                .... // 遍历元素生成列表
</SimpleScroll>)


作者:voidJay
链接:https://juejin.cn/post/7021796590214430756


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐