InfiniteVirtualList.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import React, { FC, CSSProperties } from "react";
  2. import { FixedSizeList as List } from "react-window";
  3. import InfiniteLoader from "react-window-infinite-loader";
  4. import type { SegmentDetailModel } from "@/models/datasets";
  5. import SegmentCard from "./SegmentCard";
  6. import s from "./style.module.css";
  7. type IInfiniteVirtualListProps = {
  8. hasNextPage?: boolean; // Are there more items to load? (This information comes from the most recent API request.)
  9. isNextPageLoading: boolean; // Are we currently loading a page of items? (This may be an in-flight flag in your Redux store for example.)
  10. items: Array<SegmentDetailModel[]>; // Array of items loaded so far.
  11. loadNextPage: () => Promise<any>; // Callback function responsible for loading the next page of items.
  12. onClick: (detail: SegmentDetailModel) => void;
  13. onChangeSwitch: (segId: string, enabled: boolean) => Promise<void>;
  14. };
  15. const InfiniteVirtualList: FC<IInfiniteVirtualListProps> = ({
  16. hasNextPage,
  17. isNextPageLoading,
  18. items,
  19. loadNextPage,
  20. onClick: onClickCard,
  21. onChangeSwitch,
  22. }) => {
  23. // If there are more items to be loaded then add an extra row to hold a loading indicator.
  24. const itemCount = hasNextPage ? items.length + 1 : items.length;
  25. // Only load 1 page of items at a time.
  26. // Pass an empty callback to InfiniteLoader in case it asks us to load more than once.
  27. const loadMoreItems = isNextPageLoading ? () => { } : loadNextPage;
  28. // Every row is loaded except for our loading indicator row.
  29. const isItemLoaded = (index: number) => !hasNextPage || index < items.length;
  30. // Render an item or a loading indicator.
  31. const Item = ({ index, style }: { index: number; style: CSSProperties }) => {
  32. let content;
  33. if (!isItemLoaded(index)) {
  34. content = (
  35. <>
  36. {[1, 2, 3].map((v) => (
  37. <SegmentCard loading={true} detail={{ position: v } as any} />
  38. ))}
  39. </>
  40. );
  41. } else {
  42. content = items[index].map((segItem) => (
  43. <SegmentCard
  44. key={segItem.id}
  45. detail={segItem}
  46. onClick={() => onClickCard(segItem)}
  47. onChangeSwitch={onChangeSwitch}
  48. loading={false}
  49. />
  50. ));
  51. }
  52. return (
  53. <div style={style} className={s.cardWrapper}>
  54. {content}
  55. </div>
  56. );
  57. };
  58. return (
  59. <InfiniteLoader
  60. itemCount={itemCount}
  61. isItemLoaded={isItemLoaded}
  62. loadMoreItems={loadMoreItems}
  63. >
  64. {({ onItemsRendered, ref }) => (
  65. <List
  66. ref={ref}
  67. className="List"
  68. height={800}
  69. width={"100%"}
  70. itemSize={200}
  71. itemCount={itemCount}
  72. onItemsRendered={onItemsRendered}
  73. >
  74. {Item}
  75. </List>
  76. )}
  77. </InfiniteLoader>
  78. );
  79. };
  80. export default InfiniteVirtualList;