问题点

  • 如果一次加载 10 条数据,但是页面高度很高,不会显示滚动条,比如再大屏幕显示,会导致 hasMore=true
  • loader 内容会一直显示,但是不加载数据,给人卡顿的体验。

解决

  • 只要有滚动条,load 就显示正常
  • 初始化多加载些数据,比如 100 条,然后每次滚动触发 next 再次加载

无限滚动方案二
Intersection Observer API

案例参考
https://dev.to/vishnusatheesh/exploring-infinite-scroll-techniques-in-react-1bn0

react-infinite-scroll-component

https://github.com/ankeetmaini/react-infinite-scroll-component
https://www.npmjs.com/package/react-infinite-scroll-component
https://www.jianshu.com/p/4bb28664ad74

  1. // yarn add react-infinite-scroll-component
  2. import React, { useRef } from 'react'
  3. import { Skeleton, Divider } from 'antd'
  4. import InfiniteScroll from 'react-infinite-scroll-component'
  5. import clsx from 'clsx'
  6. type IProps = {
  7. children: React.ReactNode,
  8. fetchMore: () => void;
  9. dataLength: number;
  10. hasMore: boolean;
  11. loading: boolean;
  12. };
  13. function ScrollList(props: IProps){
  14. const {
  15. children,
  16. fetchMore,
  17. dataLength,
  18. hasMore = false,
  19. loading = false,
  20. className
  21. } = props;
  22. const domRef = useRef<HTMLDivElement>(null);
  23. return (
  24. <section
  25. ref={domRef}
  26. className={clsx('overflow-x-hidden overflow-y-auto ',className)}
  27. >
  28. <InfiniteScroll
  29. dataLength={dataLength}
  30. next={fetchMore}
  31. hasMore={hasMore}
  32. scrollableTarget={domRef.current}
  33. // 小于20条数据全部展示
  34. endMessage={
  35. dataLength > 20
  36. ? <Divider plain>已全部加载</Divider>
  37. : false
  38. }
  39. loader={(
  40. <Skeleton
  41. avatar={{ shape: 'square' }}
  42. paragraph={{ rows: 3 }}
  43. active
  44. className="mt16"
  45. />
  46. )}
  47. >
  48. {children}
  49. </InfiniteScroll>
  50. </section>
  51. );
  52. }
  53. export default ScrollList;

InfiniteScroll

  1. import React, { useEffect, useMemo, useState } from 'react';
  2. import { useRequest } from 'ahooks';
  3. import { ScrollList, InputSearch, isSuccess, requestOptions } from '@/components';
  4. const initPage = { page: 1, limit: 10 }
  5. function App() {
  6. const [dataSource, setDataSource] = useState([]);
  7. const [page, setPage] = useState(initPage);
  8. // 初始化数据标识
  9. const [initFlag, setInitFlag] = useState(true);
  10. // 获取列表
  11. const { data = { total: 0 }, runAsync, loading } = useRequest(getUsers, requestOptions);
  12. const hasMore = useMemo(() => dataSource.length < data.total, [dataSource, data.total]);
  13. useEffect(() => {
  14. init();
  15. }, [page, initFlag]);
  16. async function init() {
  17. const res = await runAsync(page);
  18. if (!isSuccess(res)) return;
  19. // 初始化数据
  20. if (flag) {
  21. return setSource(res.data);
  22. }
  23. // 加载更多合并数据
  24. setDataSource(prevState => prevState.concat(res.data));
  25. }
  26. // 向下滚动触发的事件,用于更新 page++
  27. async function fetchMore() {
  28. // 合并数据,不要初始化
  29. setInitFlag(false);
  30. setPage(prevState => {
  31. return { ...prevState, page: prevState.page + 1 }
  32. });
  33. }
  34. async function onSearch(name) {
  35. const payload = name ? { ...initPage, name } : initPage;
  36. // 搜索初始化数据
  37. setInitFlag(true);
  38. setPage(payload);
  39. }
  40. function renderItem(item) {
  41. return (
  42. <Col span={8} key={item.id}>
  43. <Card>{item.name}</Card>
  44. </Col>
  45. )
  46. }
  47. return (
  48. <PageContainer>
  49. <InputSearch onSearch={onSearch} />
  50. <Spin spinning={page.page === 1 ? loading : false}>
  51. <ScrollList
  52. fetchMore={fetchMore}
  53. hasMore={hasMore}
  54. dataLength={dataSource.length}
  55. >
  56. <Row gutter={[24, 24]} className="w-full">
  57. {dataSource.map(renderItem)}
  58. </Row>
  59. </ScrollList>
  60. </Spin>
  61. </PageContainer>
  62. )
  63. }
  64. export default App

推送列表滚动

https://www.jq22.com/yanshi22468
https://www.jq22.com/demo/jquerylistScroll201911081901/